]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/vblade/vblade.c
vblade: fix code so #pragma pack isnt needed
[plan9front.git] / sys / src / cmd / disk / vblade / vblade.c
1 /*
2  * vblade -- virtual aoe target
3  * copyright © 2007 erik quanstrom
4  */
5
6 #include <u.h>
7 #include <libc.h>
8 #include <thread.h>
9 #include <ip.h>                 /* irony */
10 #include <fis.h>
11
12 enum {
13         Eaddrlen        = 6,            /* only defined in kernel */
14 };
15 #include "aoe.h"
16
17 enum {
18         Fclone,
19         Fdata,
20         Flast,
21
22         Fraw    = 1<<0,
23
24         Nether  = 8,
25         Nvblade = 8,
26         Maxpkt  = 10000,
27         Hdrlba  = 128,
28         Conflen = 1024,
29 };
30
31 typedef struct {
32         int     iflag;
33         int     flag;
34         int     shelf;
35         int     slot;
36         uvlong  maxlba;
37         char    *config;
38 } Conf;
39
40 typedef struct {
41         char    magic[32];
42         char    size[32];
43         char    address[16];
44         char    configlen[6];
45         char    pad[512-32-32-16-6];
46         char    config[Conflen];
47 } Vbhdr;
48
49 typedef struct {
50         Vbhdr   hdr;
51         vlong   maxlba;
52         vlong   hdrsz;
53         int     shelf;
54         int     slot;
55         int     clen;
56         int     flag;
57         int     fd;
58 } Vblade;
59
60 static  Vblade  vblade[Nvblade];
61 static  int     nblade;
62
63 static  char    *ethertab[Nether] = {
64         "/net/ether0",
65 };
66 static  int     etheridx = 1;
67 static  int     efdtab[Nether*Flast];
68 static  char    pkttab[Nether][Maxpkt];
69 static  char    bctab[Nether][Maxpkt];
70 static  int     mtutab[Nether];
71 static  char    Magic[] = "aoe vblade\n";
72
73 static int
74 getmtu(char *p)
75 {
76         char buf[50];
77         int fd, mtu;
78
79         snprint(buf, sizeof buf, "%s/mtu", p);
80         if((fd = open(buf, OREAD)) == -1)
81                 return 2;
82         if(read(fd, buf, 36) < 0)
83                 return 2;
84         close(fd);
85         buf[36] = 0;
86         mtu = strtoul(buf+12, 0, 0)-Aoehsz;
87         return mtu>>9;
88 }
89
90 int
91 parseshelf(char *s, int *shelf, int *slot)
92 {
93         int a, b;
94
95         a = strtoul(s, &s, 0);
96         if(*s++ != '.')
97                 return -1;
98         b = strtoul(s, &s, 0);
99         if(*s != 0)
100                 return -1;
101         *shelf = a;
102         *slot = b;
103         return 0;
104 }
105
106 static vlong
107 getsize(char *s)
108 {
109         static char tab[] = "ptgmk";
110         char *p;
111         vlong v;
112
113         v = strtoull(s, &s, 0);
114         while((p = strchr(tab, *s++)) && *p)
115                 while(*p++)
116                         v *= 1024;
117         if(s[-1])
118                 return -1;
119         return v;
120 }
121
122 vlong
123 sizetolba(vlong size)
124 {
125         if(size < 512 || size & 0x1ff){
126                 fprint(2, "invalid size %lld\n", size);
127                 exits("size");
128         }
129         return size>>9;
130 }
131
132 static int
133 savevblade(int fd, Vblade *vb)
134 {
135         int n, r;
136         char *p;
137
138         sprint(vb->hdr.size, "%lld", vb->maxlba<<9);
139         sprint(vb->hdr.address, "%d.%d", vb->shelf, vb->slot);
140         sprint(vb->hdr.configlen, "%d", vb->clen);
141
142         if(vb->flag & Fraw)
143                 return 0;       
144         p = (char*)vb;
145         for(n = 0; n < sizeof *vb; n += r)
146                 if((r = pwrite(fd, p+n, sizeof *vb-n, n)) <= 0)
147                         break;
148         if(n != sizeof *vb)
149                 return -1;
150         return 0;
151 }
152
153 static char*
154 chkvblade(int fd, Vblade *vb)
155 {
156         Vbhdr *h;
157
158         h = &vb->hdr;
159         if(readn(fd, (char*)h, sizeof *h) != sizeof *h)
160                 return "bad read";
161         if(memcmp(h->magic, Magic, sizeof Magic))
162                 return "bad magic";
163         h->size[sizeof h->size-1] = 0;
164         vb->maxlba = sizetolba(strtoull(h->size, 0, 0));
165         if(parseshelf(h->address, &vb->shelf, &vb->slot) == -1)
166                 return "bad shelf";
167         h->configlen[sizeof h->configlen-1] = 0;
168         vb->clen = strtoul(h->configlen, 0, 0);
169         return 0;
170 }
171
172 void
173 checkfile(char *s, Vblade *vb, int iflag)
174 {
175         char *e;
176
177         vb->fd = open(s, ORDWR);
178         if(vb->fd == -1)
179                 sysfatal("can't open backing store: %r");
180         if(iflag == 0 && (e = chkvblade(vb->fd, vb)))
181                 sysfatal("invalid vblade %s", e);
182 }
183
184 void
185 recheck(int fd, Vblade *vb)
186 {
187         Dir *d;
188         vlong v;
189
190         d = dirfstat(fd);
191         if(d == 0)
192                 sysfatal("can't stat: %r");
193         if((vb->flag & Fraw) == 0)
194                 vb->hdrsz = Hdrlba;
195         v = sizetolba(d->length & ~0x1ff) - vb->hdrsz;
196         free(d);
197         if(vb->maxlba > v)
198                 sysfatal("cmdline size too large (%lld sector overhead)", vb->hdrsz);
199         if(vb->maxlba == 0)
200                 vb->maxlba = v;
201
202         savevblade(fd, vb);
203 }
204
205 int
206 aoeopen(char *e, int fds[])
207 {
208         char buf[128], ctl[13];
209         int n;
210
211         snprint(buf, sizeof buf, "%s/clone", e);
212         if((fds[Fclone] = open(buf, ORDWR)) == -1)
213                 return -1;
214         memset(ctl, 0, sizeof ctl);
215         if(read(fds[Fclone], ctl, sizeof ctl - 1) < 0)
216                 return -1;
217         n = atoi(ctl);
218         snprint(buf, sizeof buf, "connect %d", Aoetype);
219         if(write(fds[Fclone], buf, strlen(buf)) != strlen(buf))
220                 return -1;
221         snprint(buf, sizeof buf, "%s/%d/data", e, n);
222         fds[Fdata] = open(buf, ORDWR);
223         return fds[Fdata];
224 }
225
226 void
227 replyhdr(Aoehdr *h, Vblade *vblade)
228 {
229         uchar   ea[Eaddrlen];
230
231         memmove(ea, h->dst, Eaddrlen);
232         memmove(h->dst, h->src, Eaddrlen);
233         memmove(h->src, ea, Eaddrlen);
234
235         hnputs(h->major, vblade->shelf);
236         h->minor = vblade->slot;
237         h->verflag |= AFrsp;
238 }
239
240 static int
241 serveconfig(Aoehdr *h, Vblade *vb, int mtu)
242 {
243         int cmd, reqlen, len;
244         char *cfg;
245         Aoeqc *q;
246
247         if(memcmp(h->src, h->dst, Eaddrlen) == 0)
248                 return -1;
249
250         q = (Aoeqc*)((char*)h + Aoehsz);
251         reqlen = nhgets(q->cslen);
252         len = vb->clen;
253         cmd = q->verccmd&0xf;
254         cfg = (char*)q + Aoecfgsz;
255
256         switch(cmd){
257         case AQCtest:
258                 if(reqlen != len)
259                         return -1;
260         case AQCprefix:
261                 if(reqlen > len)
262                         return -1;
263                 if(memcmp(vb->hdr.config, cfg, reqlen) != 0)
264                         return -1;
265         case AQCread:
266                 break;
267         case AQCset:
268                 if(len && len != reqlen || memcmp(vb->hdr.config, cfg, reqlen) != 0){
269                         h->verflag |= AFerr;
270                         h->error = AEcfg;
271                         break;
272                 }
273         case AQCfset:
274                 if(reqlen > Conflen){
275                         h->verflag |= AFerr;
276                         h->error = AEarg;
277                         break;
278                 }
279                 memset(vb->hdr.config, 0, sizeof vb->hdr.config);
280                 memmove(vb->hdr.config, cfg, reqlen);
281                 vb->clen = len = reqlen;
282                 savevblade(vb->fd, vb);
283                 break;
284         default:
285                 h->verflag |= AFerr;
286                 h->error = AEarg;
287         }
288
289         memmove(cfg, vb->hdr.config, len);
290         hnputs(q->cslen, len);
291         hnputs(q->bufcnt, 24);
292         q->scnt = mtu;
293         hnputs(q->fwver, 2323);
294         q->verccmd = Aoever<<4 | cmd;
295
296         return Aoehsz+Aoecfgsz + len;
297 }
298
299 static ushort ident[256] = {
300         [47] 0x8000,
301         [49] 0x0200,
302         [50] 0x4000,
303         [83] 0x5400,
304         [84] 0x4000,
305         [86] 0x1400,
306         [87] 0x4000,
307         [93] 0x400b,
308 };
309
310 static void
311 idmoveto(char *a, int idx, int len, char *s)
312 {
313         char *p;
314
315         p = a+idx*2;
316         for(; len > 0; len -= 2) {
317                 if(*s == 0)
318                         p[1] = ' ';
319                 else
320                         p[1] = *s++;
321                 if (*s == 0)
322                         p[0] = ' ';
323                 else
324                         p[0] = *s++;
325                 p += 2;
326         }
327 }
328
329 static void
330 lbamoveto(char *p, int idx, int n, vlong lba)
331 {
332         int i;
333
334         p += idx*2;
335         for(i = 0; i < n; i++)
336                 *p++ = lba>>i*8;
337 }
338
339 enum {
340         Crd             = 0x20,
341         Crdext          = 0x24,
342         Cwr             = 0x30,
343         Cwrext          = 0x34,
344         Cid             = 0xec,
345 };
346
347 static uvlong
348 getlba(uchar *p)
349 {
350         uvlong v;
351
352         v = p[0];
353         v |= p[1]<<8;
354         v |= p[2]<<16;
355         v |= p[3]<<24;
356         v |= (uvlong)p[4]<<32;
357         v |= (uvlong)p[5]<<40;
358         return v;
359 }
360
361 static void
362 putlba(uchar *p, vlong lba)
363 {
364         p[0] = lba;
365         p[1] = lba>>8;
366         p[2] = lba>>16;
367         p[3] = lba>>24;
368         p[5] = lba>>32;
369         p[6] = lba>>40;
370 }
371
372 static int
373 serveata(Aoehdr *h, Vblade *vb, int mtu)
374 {
375         Aoeata *a;
376         char *buf;
377         int rbytes, bytes, len;
378         vlong lba, off;
379
380         a = (Aoeata*)((char*)h + Aoehsz);
381         buf = (char*)a + Aoeatasz;
382         lba = getlba(a->lba);
383         len = a->scnt<<9;
384         off = lba+vb->hdrsz<<9;
385
386         rbytes  = 0;
387         if(a->scnt > mtu || a->scnt == 0){
388                 h->verflag |= AFerr;
389                 a->cmdstat = ASdrdy|ASerr;
390                 h->error = AEarg;
391                 goto out;
392         }
393         
394         if(a->cmdstat != Cid)
395         if(lba+a->scnt > vb->maxlba){
396                 a->errfeat = Eidnf;
397                 a->cmdstat = ASdrdy|ASerr;
398                 goto out;
399         }
400
401         if(a->cmdstat&0xf0 == 0x20)
402                 lba &= 0xfffffff;
403         switch(a->cmdstat){
404         default:
405                 a->errfeat = Eabrt;
406                 a->cmdstat = ASdrdy|ASerr;
407                 goto out;
408         case Cid:
409                 memmove(buf, ident, sizeof ident);
410                 idmoveto(buf, 27, 40, "Plan 9 Vblade");
411                 idmoveto(buf, 10, 20, "serial#");
412                 idmoveto(buf, 23, 8, "2");
413                 lbamoveto(buf, 60, 4, vb->maxlba);
414                 lbamoveto(buf, 100, 8, vb->maxlba);
415                 a->cmdstat = ASdrdy;
416                 rbytes = 512;
417                 goto out;
418         case Crd:
419         case Crdext:
420                 bytes = pread(vb->fd, buf, len, off);
421                 rbytes = bytes;
422                 break;
423         case Cwr:
424         case Cwrext:
425                 bytes = pwrite(vb->fd, buf, len, off);
426                 break;
427         }
428         if(bytes != len){
429                 a->errfeat = Eabrt;
430                 a->cmdstat = ASdf|ASerr;
431                 putlba(a->lba, lba+(len-bytes)>>9);
432                 rbytes = 0;
433                 goto out;
434         }
435
436         putlba(a->lba, lba+a->scnt);
437         a->scnt = 0;
438         a->errfeat = 0;
439         a->cmdstat = ASdrdy;
440 out:
441         return Aoehsz+Aoeatasz + rbytes;
442 }
443
444 static int
445 myea(uchar ea[6], char *p)
446 {
447         char buf[50];
448         int fd;
449
450         snprint(buf, sizeof buf, "%s/addr", p);
451         if((fd = open(buf, OREAD)) == -1)
452                 return -1;
453         if(read(fd, buf, 12) < 12)
454                 return -1;
455         close(fd);
456         return parseether(ea, buf);
457 }
458
459 static void
460 bcastpkt(Aoehdr *h, uint shelf, uint slot, int i)
461 {
462         myea(h->dst, ethertab[i]);
463         memset(h->src, 0xff, Eaddrlen);
464         hnputs(h->type, Aoetype);
465         hnputs(h->major, shelf);
466         h->minor = slot;
467         h->cmd = ACconfig;
468         h->tag[0] = h->tag[1] = h->tag[2] = h->tag[3] = 0;
469 }
470
471 int
472 bladereply(Vblade *v, int i, int fd, char *pkt)
473 {
474         int n;
475         Aoehdr *h;
476
477         h = (Aoehdr*)pkt;
478         switch(h->cmd){
479         case ACata:
480                 n = serveata(h, v, mtutab[i]);
481                 break;
482         case ACconfig:
483                 n = serveconfig(h, v, mtutab[i]);
484                 break;
485         default:
486                 n = -1;
487                 break;
488         }
489         if(n == -1)
490                 return -1;
491         replyhdr(h, v);
492         if(n < 60){
493                 memset(pkt+n, 0, 60-n);
494                 n = 60;
495         }
496         if(write(fd, h, n) != n){
497                 fprint(2, "write to %s failed: %r\n", ethertab[i]);
498                 return -1;
499         }
500         return 0;
501 }
502
503 void
504 serve(void *v)
505 {
506         int i, j, popcnt, vec, n, s, efd;
507         char *pkt, *bcpkt;
508         Aoehdr *h;
509
510 fmtinstall('E', eipfmt);
511         i = (int)(uintptr)v;
512
513         efd = efdtab[i*Flast+Fdata];
514         pkt = pkttab[i];
515         bcpkt = bctab[i];
516
517         n = 60;
518         h = (Aoehdr*)pkt;
519         bcastpkt(h, 0xffff, 0xff, i);
520         goto start;
521
522         for(;;){
523                 n = read(efd, pkt, Maxpkt);
524         start:
525                 if(n < 60 || h->verflag & AFrsp)
526                         continue;
527                 s = nhgets(h->major);
528                 popcnt = 0;
529                 vec = 0;
530                 for(j = 0; j < nblade; j++){
531                         if((vblade[j].shelf == s || s == 0xffff)
532                         && (vblade[j].slot == h->minor || h->minor == 0xff)){
533                                 popcnt++;
534                                 vec |= 1<<j;
535                         }
536                 }
537                 for(j = 0; popcnt>0 && j < nblade; j++){
538                         if((vec & 1<<j) == 0)
539                                 continue;
540                         if(popcnt>0){
541                                 memcpy(bcpkt, pkt, n);
542                                 bladereply(vblade + j, i, efd, bcpkt);
543                         }else
544                                 bladereply(vblade + j, i, efd, pkt);
545                         popcnt--;
546                 }
547         }
548 }
549
550 void
551 launch(char *tab[], int fdtab[])
552 {
553         int i;
554
555         for(i = 0; tab[i]; i++){
556                 if(aoeopen(tab[i], fdtab+Flast*i) < 0)
557                         sysfatal("network open: %r");
558                 /*
559                  * use proc not threads.  otherwise we will block on read/write.
560                  */
561                 proccreate(serve, (void*)i, 32*1024);
562         }
563 }
564
565 void
566 usage(void)
567 {
568         fprint(2, "vblade [-ir] [-s size] [-a shelf.slot] [-c config] [-e ether] file\n");
569         exits("usage");
570 }
571
572 void
573 goblade(Vblade *vblade, char *file, Conf *c)
574 {
575         char *anal;
576
577         if(c->iflag == 1)
578                 memcpy(vblade->hdr.magic, Magic, sizeof Magic);
579         checkfile(file, vblade, c->iflag);
580
581         vblade->flag = c->flag;
582         if(c->shelf != -1){
583                 vblade->shelf = c->shelf;
584                 vblade->slot = c->slot;
585         }
586         if(c->maxlba > 0)
587                 vblade->maxlba = c->maxlba;
588         if(c->config != nil)
589                 memmove(vblade->hdr.config, c->config, vblade->clen = strlen(c->config));
590
591         recheck(vblade->fd, vblade);
592
593         anal = "";
594         if(vblade->maxlba > 1)
595                 anal = "s";
596         fprint(2, "lblade %d.%d %lld sector%s\n", vblade->shelf, vblade->slot, vblade->maxlba, anal);
597 }
598
599 void
600 threadmain(int argc, char **argv)
601 {
602         int i, lastc, anye;
603         Conf c;
604
605         anye = 0;
606         for(;;){
607                 if(nblade == nelem(vblade))
608                         sysfatal("too many blades");
609                 c = (Conf){0, 0, -1, -1, 0, nil};
610                 lastc = 0;
611                 ARGBEGIN{
612                 case 'a':
613                         lastc = 'a';
614                         if(parseshelf(EARGF(usage()), &c.shelf, &c.slot) == -1)
615                                 sysfatal("bad vblade address");
616                         break;
617                 case 'c':
618                         lastc = 'c';
619                         c.config = EARGF(usage());
620                         break;
621                 case 'e':
622                         lastc = 'e';
623                         if(anye++ == 0)
624                                 etheridx = 0;
625                         if(etheridx == nelem(ethertab))
626                                 sysfatal("too many interfaces");
627                         ethertab[etheridx++] = EARGF(usage());
628                         break;
629                 case 'i':
630                         lastc = 'i';
631                         c.iflag = 1;
632                         break;
633                 case 'r':
634                         lastc = 'r';
635                         c.flag |= Fraw;
636                         c.iflag = 1;
637                         break;
638                 case 's':
639                         lastc = 's';
640                         c.maxlba = sizetolba(getsize(EARGF(usage())));
641                         break;
642                 default:
643                         lastc = '?';
644                         usage();
645                 }ARGEND;
646
647                 if(argc == 0 && lastc == 'e')
648                         break;
649                 if(argc == 0)
650                         usage();
651                 goblade(vblade + nblade++, *argv, &c);
652                 if(argc == 1)
653                         break;
654         }
655
656         if(nblade == 0)
657                 usage();        
658         for(i = 0; i < etheridx; i++)
659                 mtutab[i] = getmtu(ethertab[i]);
660
661         launch(ethertab, efdtab);
662
663         for(; sleep(1*1000) != -1;)
664                 ;
665         threadexitsall("interrupted");
666 }