1 /* 2 * © 2005-2010 coraid 3 * ATA-over-Ethernet (AoE) storage initiator 4 */ 5 6 #include "u.h" 7 #include "../port/lib.h" 8 #include "mem.h" 9 #include "dat.h" 10 #include "fns.h" 11 #include "io.h" 12 #include "ureg.h" 13 #include "../port/error.h" 14 #include "../port/netif.h" 15 #include "etherif.h" 16 #include "../ip/ip.h" 17 #include "../port/aoe.h" 18 19 #pragma varargck argpos eventlog 1 20 21 #define dprint(...) if(debug) eventlog(__VA_ARGS__); else USED(debug); 22 #define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); 23 24 enum { 25 Maxunits = 0xff, 26 Maxframes = 128, 27 Ndevlink = 6, 28 Nea = 6, 29 Nnetlink = 6, 30 }; 31 32 #define TYPE(q) ((ulong)(q).path & 0xf) 33 #define UNIT(q) (((ulong)(q).path>>4) & 0xff) 34 #define L(q) (((ulong)(q).path>>12) & 0xf) 35 #define QID(u, t) ((u)<<4 | (t)) 36 #define Q3(l, u, t) ((l)<<8 | QID(u, t)) 37 #define UP(d) ((d)->flag & Dup) 38 /* 39 * would like this to depend on the chan (srb). 40 * not possible in the current structure. 41 */ 42 #define Nofail(d, s) ((d)->flag & Dnofail) 43 44 #define MS2TK(t) ((t)/MS2HZ) 45 46 enum { 47 Qzero, 48 Qtopdir = 1, 49 Qtopbase, 50 Qtopctl = Qtopbase, 51 Qtoplog, 52 Qtopend, 53 54 Qunitdir, 55 Qunitbase, 56 Qctl = Qunitbase, 57 Qdata, 58 Qconfig, 59 Qident, 60 61 Qdevlinkdir, 62 Qdevlinkbase, 63 Qdevlink = Qdevlinkbase, 64 Qdevlinkend, 65 66 Qtopfiles = Qtopend-Qtopbase, 67 Qdevlinkfiles = Qdevlinkend-Qdevlinkbase, 68 69 Eventlen = 256, 70 Nevents = 64, /* must be power of 2 */ 71 72 Fread = 0, 73 Fwrite, 74 Tfree = -1, 75 Tmgmt, 76 77 /* 78 * round trip bounds, timeouts, in ticks. 79 * timeouts should be long enough that rebooting 80 * the coraid (which usually takes under two minutes) 81 * doesn't trigger a timeout. 82 */ 83 Rtmax = MS2TK(320), 84 Rtmin = MS2TK(20), 85 Maxreqticks = 4*60*HZ, /* was 45*HZ */ 86 87 Dbcnt = 1024, 88 89 Crd = 0x20, 90 Crdext = 0x24, 91 Cwr = 0x30, 92 Cwrext = 0x34, 93 Cid = 0xec, 94 }; 95 96 enum { 97 Read, 98 Write, 99 }; 100 101 /* 102 * unified set of flags 103 * a Netlink + Aoedev most both be jumbo capable 104 * to send jumbograms to that interface. 105 */ 106 enum { 107 /* sync with ahci.h */ 108 Dllba = 1<<0, 109 Dsmart = 1<<1, 110 Dpower = 1<<2, 111 Dnop = 1<<3, 112 Datapi = 1<<4, 113 Datapi16= 1<<5, 114 115 /* aoe specific */ 116 Dup = 1<<6, 117 Djumbo = 1<<7, 118 Dnofail = 1<<8, 119 }; 120 121 static char *flagname[] = { 122 "llba", 123 "smart", 124 "power", 125 "nop", 126 "atapi", 127 "atapi16", 128 129 "up", 130 "jumbo", 131 "nofail", 132 }; 133 134 typedef struct { 135 ushort flag; 136 uint lostjumbo; 137 int datamtu; 138 139 Chan *cc; 140 Chan *dc; 141 Chan *mtu; /* open early to prevent bind issues. */ 142 char path[Maxpath]; 143 uchar ea[Eaddrlen]; 144 } Netlink; 145 146 typedef struct { 147 Netlink *nl; 148 int nea; 149 ulong eaidx; 150 uchar eatab[Nea][Eaddrlen]; 151 ulong npkt; 152 ulong resent; 153 ushort flag; 154 155 ulong rttavg; 156 ulong mintimer; 157 } Devlink; 158 159 typedef struct Srb Srb; 160 struct Srb { 161 Rendez; 162 Srb *next; 163 ulong ticksent; 164 ulong len; 165 vlong sector; 166 short write; 167 short nout; 168 char *error; 169 void *dp; 170 void *data; 171 }; 172 173 typedef struct { 174 int tag; 175 ulong bcnt; 176 ulong dlen; 177 vlong lba; 178 ulong ticksent; 179 int nhdr; 180 uchar hdr[ETHERMINTU]; 181 void *dp; 182 Devlink *dl; 183 Netlink *nl; 184 int eaidx; 185 Srb *srb; 186 } Frame; 187 188 typedef struct Aoedev Aoedev; 189 struct Aoedev { 190 QLock; 191 Aoedev *next; 192 193 ulong vers; 194 195 int ndl; 196 ulong dlidx; 197 Devlink *dl; 198 Devlink dltab[Ndevlink]; 199 200 ushort fwver; 201 ushort flag; 202 int nopen; 203 int major; 204 int minor; 205 int unit; 206 int lasttag; 207 int nframes; 208 Frame *frames; 209 vlong bsize; 210 vlong realbsize; 211 212 uint maxbcnt; 213 ushort nout; 214 ushort maxout; 215 ulong lastwadj; 216 Srb *head; 217 Srb *tail; 218 Srb *inprocess; 219 220 /* magic numbers 'R' us */ 221 char serial[20+1]; 222 char firmware[8+1]; 223 char model[40+1]; 224 int nconfig; 225 uchar config[1024]; 226 uchar ident[512]; 227 }; 228 229 #pragma varargck type "æ" Aoedev* 230 231 static struct { 232 Lock; 233 QLock; 234 Rendez; 235 char buf[Eventlen*Nevents]; 236 char *rp; 237 char *wp; 238 } events; 239 240 static struct { 241 RWlock; 242 int nd; 243 Aoedev *d; 244 } devs; 245 246 static struct { 247 Lock; 248 int reader[Nnetlink]; /* reader is running. */ 249 Rendez rendez[Nnetlink]; /* confirm exit. */ 250 Netlink nl[Nnetlink]; 251 } netlinks; 252 253 extern Dev aoedevtab; 254 static Ref units; 255 static Ref drivevers; 256 static int debug; 257 static int autodiscover = 1; 258 static int rediscover; 259 260 char Enotup[] = "aoe device is down"; 261 char Echange[] = "media or partition has changed"; 262 263 static Srb* 264 srballoc(ulong sz) 265 { 266 Srb *srb; 267 268 srb = malloc(sizeof *srb+sz); 269 if(srb == nil) 270 error(Enomem); 271 srb->dp = srb->data = srb+1; 272 srb->ticksent = MACHP(0)->ticks; 273 return srb; 274 } 275 276 static Srb* 277 srbkalloc(void *db, ulong) 278 { 279 Srb *srb; 280 281 srb = malloc(sizeof *srb); 282 if(srb == nil) 283 error(Enomem); 284 srb->dp = srb->data = db; 285 srb->ticksent = MACHP(0)->ticks; 286 return srb; 287 } 288 289 #define srbfree(srb) free(srb) 290 291 static void 292 srberror(Srb *srb, char *s) 293 { 294 srb->error = s; 295 srb->nout--; 296 if (srb->nout == 0) 297 wakeup(srb); 298 } 299 300 static void 301 frameerror(Aoedev *d, Frame *f, char *s) 302 { 303 Srb *srb; 304 305 srb = f->srb; 306 if(f->tag == Tfree || !srb) 307 return; 308 f->srb = nil; 309 f->tag = Tfree; /* don't get fooled by way-slow responses */ 310 srberror(srb, s); 311 d->nout--; 312 } 313 314 static char* 315 unitname(Aoedev *d) 316 { 317 uprint("%d.%d", d->major, d->minor); 318 return up->genbuf; 319 } 320 321 static int 322 eventlogready(void*) 323 { 324 return *events.rp; 325 } 326 327 static long 328 eventlogread(void *a, long n) 329 { 330 int len; 331 char *p, *buf; 332 333 buf = smalloc(Eventlen); 334 qlock(&events); 335 lock(&events); 336 p = events.rp; 337 len = *p; 338 if(len == 0){ 339 n = 0; 340 unlock(&events); 341 } else { 342 if(n > len) 343 n = len; 344 /* can't move directly into pageable space with events lock held */ 345 memmove(buf, p+1, n); 346 *p = 0; 347 events.rp = p += Eventlen; 348 if(p >= events.buf + sizeof events.buf) 349 events.rp = events.buf; 350 unlock(&events); 351 352 /* the concern here is page faults in memmove below */ 353 if(waserror()){ 354 free(buf); 355 qunlock(&events); 356 nexterror(); 357 } 358 memmove(a, buf, n); 359 poperror(); 360 } 361 free(buf); 362 qunlock(&events); 363 return n; 364 } 365 366 static int 367 eventlog(char *fmt, ...) 368 { 369 int dragrp, n; 370 char *p; 371 va_list arg; 372 373 lock(&events); 374 p = events.wp; 375 dragrp = *p++; 376 va_start(arg, fmt); 377 n = vsnprint(p, Eventlen-1, fmt, arg); 378 *--p = n; 379 p = events.wp += Eventlen; 380 if(p >= events.buf + sizeof events.buf) 381 p = events.wp = events.buf; 382 if(dragrp) 383 events.rp = p; 384 unlock(&events); 385 wakeup(&events); 386 return n; 387 } 388 389 static int 390 eventcount(void) 391 { 392 int n; 393 394 lock(&events); 395 if(*events.rp == 0) 396 n = 0; 397 else 398 n = (events.wp - events.rp) & (Nevents - 1); 399 unlock(&events); 400 return n/Eventlen; 401 } 402 403 static int 404 tsince(int tag) 405 { 406 int n; 407 408 n = MACHP(0)->ticks & 0xffff; 409 n -= tag & 0xffff; 410 if(n < 0) 411 n += 1<<16; 412 return n; 413 } 414 415 static int 416 newtag(Aoedev *d) 417 { 418 int t; 419 420 do { 421 t = ++d->lasttag << 16; 422 t |= MACHP(0)->ticks & 0xffff; 423 } while (t == Tfree || t == Tmgmt); 424 return t; 425 } 426 427 static void 428 downdev(Aoedev *d, char *err) 429 { 430 Frame *f, *e; 431 432 d->flag &= ~Dup; 433 f = d->frames; 434 e = f + d->nframes; 435 for(; f < e; f->tag = Tfree, f->srb = nil, f++) 436 frameerror(d, f, Enotup); 437 d->inprocess = nil; 438 eventlog("%æ: removed; %s\n", d, err); 439 } 440 441 static Block* 442 allocfb(Frame *f) 443 { 444 int len; 445 Block *b; 446 447 len = f->nhdr + f->dlen; 448 if(len < ETHERMINTU) 449 len = ETHERMINTU; 450 b = allocb(len); 451 memmove(b->wp, f->hdr, f->nhdr); 452 if(f->dlen) 453 memmove(b->wp + f->nhdr, f->dp, f->dlen); 454 b->wp += len; 455 return b; 456 } 457 458 static void 459 putlba(Aoeata *a, vlong lba) 460 { 461 uchar *c; 462 463 c = a->lba; 464 c[0] = lba; 465 c[1] = lba >> 8; 466 c[2] = lba >> 16; 467 c[3] = lba >> 24; 468 c[4] = lba >> 32; 469 c[5] = lba >> 40; 470 } 471 472 static Devlink* 473 pickdevlink(Aoedev *d) 474 { 475 ulong i, n; 476 Devlink *l; 477 478 for(i = 0; i < d->ndl; i++){ 479 n = d->dlidx++ % d->ndl; 480 l = d->dl + n; 481 if(l && l->flag & Dup) 482 return l; 483 } 484 return 0; 485 } 486 487 static int 488 pickea(Devlink *l) 489 { 490 if(l == 0) 491 return -1; 492 if(l->nea == 0) 493 return -1; 494 return l->eaidx++ % l->nea; 495 } 496 497 static int 498 hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) 499 { 500 int i; 501 Devlink *l; 502 503 if(f->srb && MACHP(0)->ticks - f->srb->ticksent > Maxreqticks){ 504 eventlog("%æ: srb timeout\n", d); 505 if(cmd == ACata && f->srb && Nofail(d, s)) 506 f->srb->ticksent = MACHP(0)->ticks; 507 else 508 frameerror(d, f, Etimedout); 509 return -1; 510 } 511 l = pickdevlink(d); 512 i = pickea(l); 513 if(i == -1){ 514 if(cmd != ACata || f->srb == nil || !Nofail(d, s)) 515 downdev(d, "resend fails; no netlink/ea"); 516 return -1; 517 } 518 memmove(h->dst, l->eatab[i], Eaddrlen); 519 memmove(h->src, l->nl->ea, sizeof h->src); 520 hnputs(h->type, Aoetype); 521 h->verflag = Aoever << 4; 522 h->error = 0; 523 hnputs(h->major, d->major); 524 h->minor = d->minor; 525 h->cmd = cmd; 526 527 hnputl(h->tag, f->tag = newtag(d)); 528 f->dl = l; 529 f->nl = l->nl; 530 f->eaidx = i; 531 f->ticksent = MACHP(0)->ticks; 532 533 return f->tag; 534 } 535 536 static int 537 resend(Aoedev *d, Frame *f) 538 { 539 ulong n; 540 Aoeata *a; 541 542 a = (Aoeata*)f->hdr; 543 if(hset(d, f, a, a->cmd) == -1) 544 return -1; 545 n = f->bcnt; 546 if(n > d->maxbcnt){ 547 n = d->maxbcnt; /* mtu mismatch (jumbo fail?) */ 548 if(f->dlen > n) 549 f->dlen = n; 550 } 551 a->scnt = n / Aoesectsz; 552 f->dl->resent++; 553 f->dl->npkt++; 554 if(waserror()) 555 return -1; 556 devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); 557 poperror(); 558 return 0; 559 } 560 561 static void 562 discover(int major, int minor) 563 { 564 Aoehdr *h; 565 Block *b; 566 Netlink *nl, *e; 567 568 nl = netlinks.nl; 569 e = nl + nelem(netlinks.nl); 570 for(; nl < e; nl++){ 571 if(nl->cc == nil) 572 continue; 573 b = allocb(ETHERMINTU); 574 if(waserror()){ 575 freeb(b); 576 nexterror(); 577 } 578 b->wp = b->rp + ETHERMINTU; 579 memset(b->rp, 0, ETHERMINTU); 580 h = (Aoehdr*)b->rp; 581 memset(h->dst, 0xff, sizeof h->dst); 582 memmove(h->src, nl->ea, sizeof h->src); 583 hnputs(h->type, Aoetype); 584 h->verflag = Aoever << 4; 585 hnputs(h->major, major); 586 h->minor = minor; 587 h->cmd = ACconfig; 588 poperror(); 589 /* send b down the queue */ 590 devtab[nl->dc->type]->bwrite(nl->dc, b, 0); 591 } 592 } 593 594 /* 595 * Check all frames on device and resend any frames that have been 596 * outstanding for 200% of the device round trip time average. 597 */ 598 static void 599 aoesweepproc(void*) 600 { 601 ulong i, tx, timeout, nbc; 602 vlong starttick; 603 enum { Nms = 100, Nbcms = 30*1000, }; /* magic */ 604 uchar *ea; 605 Aoeata *a; 606 Aoedev *d; 607 Devlink *l; 608 Frame *f, *e; 609 610 nbc = Nbcms/Nms; 611 loop: 612 if(nbc-- == 0){ 613 if(rediscover && !waserror()){ 614 discover(0xffff, 0xff); 615 poperror(); 616 } 617 nbc = Nbcms/Nms; 618 } 619 starttick = MACHP(0)->ticks; 620 rlock(&devs); 621 for(d = devs.d; d; d = d->next){ 622 if(!canqlock(d)) 623 continue; 624 if(!UP(d)){ 625 qunlock(d); 626 continue; 627 } 628 tx = 0; 629 f = d->frames; 630 e = f + d->nframes; 631 for (; f < e; f++){ 632 if(f->tag == Tfree) 633 continue; 634 l = f->dl; 635 timeout = l->rttavg << 1; 636 i = tsince(f->tag); 637 if(i < timeout) 638 continue; 639 if(d->nout == d->maxout){ 640 if(d->maxout > 1) 641 d->maxout--; 642 d->lastwadj = MACHP(0)->ticks; 643 } 644 a = (Aoeata*)f->hdr; 645 if(a->scnt > Dbcnt / Aoesectsz && 646 ++f->nl->lostjumbo > (d->nframes << 1)){ 647 ea = f->dl->eatab[f->eaidx]; 648 eventlog("%æ: jumbo failure on %s:%E; lba%lld\n", 649 d, f->nl->path, ea, f->lba); 650 d->maxbcnt = Dbcnt; 651 d->flag &= ~Djumbo; 652 } 653 resend(d, f); 654 if(tx++ == 0){ 655 if((l->rttavg <<= 1) > Rtmax) 656 l->rttavg = Rtmax; 657 eventlog("%æ: rtt %ldms\n", d, TK2MS(l->rttavg)); 658 } 659 } 660 if(d->nout == d->maxout && d->maxout < d->nframes && 661 TK2MS(MACHP(0)->ticks - d->lastwadj) > 10*1000){ /* more magic */ 662 d->maxout++; 663 d->lastwadj = MACHP(0)->ticks; 664 } 665 qunlock(d); 666 } 667 runlock(&devs); 668 i = Nms - TK2MS(MACHP(0)->ticks - starttick); 669 if(i > 0) 670 tsleep(&up->sleep, return0, 0, i); 671 goto loop; 672 } 673 674 static int 675 fmtæ(Fmt *f) 676 { 677 char buf[16]; 678 Aoedev *d; 679 680 d = va_arg(f->args, Aoedev*); 681 snprint(buf, sizeof buf, "aoe%d.%d", d->major, d->minor); 682 return fmtstrcpy(f, buf); 683 } 684 685 static void netbind(char *path); 686 687 static void 688 aoecfg(void) 689 { 690 int n, i; 691 char *p, *f[32], buf[24]; 692 693 if((p = getconf("aoeif")) == nil || (n = tokenize(p, f, nelem(f))) < 1) 694 return; 695 /* goo! */ 696 for(i = 0; i < n; i++){ 697 p = f[i]; 698 if(strncmp(p, "ether", 5) == 0) 699 snprint(buf, sizeof buf, "#l%c/ether%c", p[5], p[5]); 700 else if(strncmp(p, "#l", 2) == 0) 701 snprint(buf, sizeof buf, "#l%c/ether%c", p[2], p[2]); 702 else 703 continue; 704 if(!waserror()){ 705 netbind(buf); 706 poperror(); 707 } 708 } 709 } 710 711 static void 712 aoeinit(void) 713 { 714 static int init; 715 static QLock l; 716 717 if(!canqlock(&l)) 718 return; 719 if(init == 0){ 720 fmtinstall(L'æ', fmtæ); 721 events.rp = events.wp = events.buf; 722 kproc("aoesweep", aoesweepproc, nil); 723 aoecfg(); 724 init = 1; 725 } 726 qunlock(&l); 727 } 728 729 static Chan* 730 aoeattach(char *spec) 731 { 732 Chan *c; 733 734 if(*spec) 735 error(Enonexist); 736 aoeinit(); 737 c = devattach(L'æ', spec); 738 mkqid(&c->qid, Qzero, 0, QTDIR); 739 return c; 740 } 741 742 static Aoedev* 743 unit2dev(ulong unit) 744 { 745 int i; 746 Aoedev *d; 747 748 rlock(&devs); 749 i = 0; 750 for(d = devs.d; d; d = d->next) 751 if(i++ == unit){ 752 runlock(&devs); 753 return d; 754 } 755 runlock(&devs); 756 uprint("unit lookup failure: %lux pc %#p", unit, getcallerpc(&unit)); 757 error(up->genbuf); 758 return nil; 759 } 760 761 static int 762 unitgen(Chan *c, ulong type, Dir *dp) 763 { 764 int perm, t; 765 ulong vers; 766 vlong size; 767 char *p; 768 Aoedev *d; 769 Qid q; 770 771 d = unit2dev(UNIT(c->qid)); 772 perm = 0644; 773 size = 0; 774 vers = d->vers; 775 t = QTFILE; 776 777 switch(type){ 778 default: 779 return -1; 780 case Qctl: 781 p = "ctl"; 782 break; 783 case Qdata: 784 p = "data"; 785 perm = 0640; 786 if(UP(d)) 787 size = d->bsize; 788 break; 789 case Qconfig: 790 p = "config"; 791 if(UP(d)) 792 size = d->nconfig; 793 break; 794 case Qident: 795 p = "ident"; 796 if(UP(d)) 797 size = sizeof d->ident; 798 break; 799 case Qdevlinkdir: 800 p = "devlink"; 801 t = QTDIR; 802 perm = 0555; 803 break; 804 } 805 mkqid(&q, QID(UNIT(c->qid), type), vers, t); 806 devdir(c, q, p, size, eve, perm, dp); 807 return 1; 808 } 809 810 static int 811 topgen(Chan *c, ulong type, Dir *d) 812 { 813 int perm; 814 vlong size; 815 char *p; 816 Qid q; 817 818 perm = 0444; 819 size = 0; 820 switch(type){ 821 default: 822 return -1; 823 case Qtopctl: 824 p = "ctl"; 825 perm = 0644; 826 break; 827 case Qtoplog: 828 p = "log"; 829 size = eventcount(); 830 break; 831 } 832 mkqid(&q, type, 0, QTFILE); 833 devdir(c, q, p, size, eve, perm, d); 834 return 1; 835 } 836 837 static int 838 aoegen(Chan *c, char *, Dirtab *, int, int s, Dir *dp) 839 { 840 int i; 841 Aoedev *d; 842 Qid q; 843 844 if(c->qid.path == 0){ 845 switch(s){ 846 case DEVDOTDOT: 847 q.path = 0; 848 q.type = QTDIR; 849 devdir(c, q, "#æ", 0, eve, 0555, dp); 850 break; 851 case 0: 852 q.path = Qtopdir; 853 q.type = QTDIR; 854 devdir(c, q, "aoe", 0, eve, 0555, dp); 855 break; 856 default: 857 return -1; 858 } 859 return 1; 860 } 861 862 switch(TYPE(c->qid)){ 863 default: 864 return -1; 865 case Qtopdir: 866 if(s == DEVDOTDOT){ 867 mkqid(&q, Qzero, 0, QTDIR); 868 devdir(c, q, "aoe", 0, eve, 0555, dp); 869 return 1; 870 } 871 if(s < Qtopfiles) 872 return topgen(c, Qtopbase + s, dp); 873 s -= Qtopfiles; 874 if(s >= units.ref) 875 return -1; 876 mkqid(&q, QID(s, Qunitdir), 0, QTDIR); 877 d = unit2dev(s); 878 assert(d != nil); 879 devdir(c, q, unitname(d), 0, eve, 0555, dp); 880 return 1; 881 case Qtopctl: 882 case Qtoplog: 883 return topgen(c, TYPE(c->qid), dp); 884 case Qunitdir: 885 if(s == DEVDOTDOT){ 886 mkqid(&q, QID(0, Qtopdir), 0, QTDIR); 887 uprint("%uld", UNIT(c->qid)); 888 devdir(c, q, up->genbuf, 0, eve, 0555, dp); 889 return 1; 890 } 891 return unitgen(c, Qunitbase+s, dp); 892 case Qctl: 893 case Qdata: 894 case Qconfig: 895 case Qident: 896 return unitgen(c, TYPE(c->qid), dp); 897 case Qdevlinkdir: 898 i = UNIT(c->qid); 899 if(s == DEVDOTDOT){ 900 mkqid(&q, QID(i, Qunitdir), 0, QTDIR); 901 devdir(c, q, "devlink", 0, eve, 0555, dp); 902 return 1; 903 } 904 if(i >= units.ref) 905 return -1; 906 d = unit2dev(i); 907 if(s >= d->ndl) 908 return -1; 909 uprint("%d", s); 910 mkqid(&q, Q3(s, i, Qdevlink), 0, QTFILE); 911 devdir(c, q, up->genbuf, 0, eve, 0755, dp); 912 return 1; 913 case Qdevlink: 914 uprint("%d", s); 915 mkqid(&q, Q3(s, UNIT(c->qid), Qdevlink), 0, QTFILE); 916 devdir(c, q, up->genbuf, 0, eve, 0755, dp); 917 return 1; 918 } 919 } 920 921 static Walkqid* 922 aoewalk(Chan *c, Chan *nc, char **name, int nname) 923 { 924 return devwalk(c, nc, name, nname, nil, 0, aoegen); 925 } 926 927 static int 928 aoestat(Chan *c, uchar *db, int n) 929 { 930 return devstat(c, db, n, nil, 0, aoegen); 931 } 932 933 static Chan* 934 aoeopen(Chan *c, int omode) 935 { 936 Aoedev *d; 937 938 if(TYPE(c->qid) != Qdata) 939 return devopen(c, omode, 0, 0, aoegen); 940 941 d = unit2dev(UNIT(c->qid)); 942 qlock(d); 943 if(waserror()){ 944 qunlock(d); 945 nexterror(); 946 } 947 if(!UP(d)) 948 error(Enotup); 949 c = devopen(c, omode, 0, 0, aoegen); 950 d->nopen++; 951 poperror(); 952 qunlock(d); 953 return c; 954 } 955 956 static void 957 aoeclose(Chan *c) 958 { 959 Aoedev *d; 960 961 if(TYPE(c->qid) != Qdata || (c->flag&COPEN) == 0) 962 return; 963 964 d = unit2dev(UNIT(c->qid)); 965 qlock(d); 966 if(--d->nopen == 0 && !waserror()){ 967 discover(d->major, d->minor); 968 poperror(); 969 } 970 qunlock(d); 971 } 972 973 static void 974 atarw(Aoedev *d, Frame *f) 975 { 976 ulong bcnt; 977 char extbit, writebit; 978 Aoeata *ah; 979 Srb *srb; 980 981 extbit = 0x4; 982 writebit = 0x10; 983 984 srb = d->inprocess; 985 bcnt = d->maxbcnt; 986 if(bcnt > srb->len) 987 bcnt = srb->len; 988 f->nhdr = AOEATASZ; 989 memset(f->hdr, 0, f->nhdr); 990 ah = (Aoeata*)f->hdr; 991 if(hset(d, f, ah, ACata) == -1) { 992 d->inprocess = nil; 993 return; 994 } 995 f->dp = srb->dp; 996 f->bcnt = bcnt; 997 f->lba = srb->sector; 998 f->srb = srb; 999 1000 ah->scnt = bcnt / Aoesectsz; 1001 putlba(ah, f->lba); 1002 if(d->flag & Dllba) 1003 ah->aflag |= AAFext; 1004 else { 1005 extbit = 0; 1006 ah->lba[3] &= 0x0f; 1007 ah->lba[3] |= 0xe0; /* LBA bit+obsolete 0xa0 */ 1008 } 1009 if(srb->write){ 1010 ah->aflag |= AAFwrite; 1011 f->dlen = bcnt; 1012 }else{ 1013 writebit = 0; 1014 f->dlen = 0; 1015 } 1016 ah->cmdstat = 0x20 | writebit | extbit; 1017 1018 /* mark tracking fields and load out */ 1019 srb->nout++; 1020 srb->dp = (uchar*)srb->dp + bcnt; 1021 srb->len -= bcnt; 1022 srb->sector += bcnt / Aoesectsz; 1023 if(srb->len == 0) 1024 d->inprocess = nil; 1025 d->nout++; 1026 f->dl->npkt++; 1027 if(waserror()){ 1028 f->tag = Tfree; 1029 d->inprocess = nil; 1030 nexterror(); 1031 } 1032 devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); 1033 poperror(); 1034 } 1035 1036 static char* 1037 aoeerror(Aoehdr *h) 1038 { 1039 int n; 1040 static char *errs[] = { 1041 "aoe protocol error: unknown", 1042 "aoe protocol error: bad command code", 1043 "aoe protocol error: bad argument param", 1044 "aoe protocol error: device unavailable", 1045 "aoe protocol error: config string present", 1046 "aoe protocol error: unsupported version", 1047 "aoe protocol error: target is reserved", 1048 }; 1049 1050 if((h->verflag & AFerr) == 0) 1051 return 0; 1052 n = h->error; 1053 if(n > nelem(errs)) 1054 n = 0; 1055 return errs[n]; 1056 } 1057 1058 static void 1059 rtupdate(Devlink *l, int rtt) 1060 { 1061 int n; 1062 1063 n = rtt; 1064 if(rtt < 0){ 1065 n = -rtt; 1066 if(n < Rtmin) 1067 n = Rtmin; 1068 else if(n > Rtmax) 1069 n = Rtmax; 1070 l->mintimer += (n - l->mintimer) >> 1; 1071 } else if(n < l->mintimer) 1072 n = l->mintimer; 1073 else if(n > Rtmax) 1074 n = Rtmax; 1075 1076 /* g == .25; cf. Congestion Avoidance and Control, Jacobson&Karels; 1988 */ 1077 n -= l->rttavg; 1078 l->rttavg += n >> 2; 1079 } 1080 1081 static int 1082 srbready(void *v) 1083 { 1084 Srb *s; 1085 1086 s = v; 1087 return s->error || (!s->nout && !s->len); 1088 } 1089 1090 static Frame* 1091 getframe(Aoedev *d, int tag) 1092 { 1093 Frame *f, *e; 1094 1095 f = d->frames; 1096 e = f + d->nframes; 1097 for(; f < e; f++) 1098 if(f->tag == tag) 1099 return f; 1100 return nil; 1101 } 1102 1103 static Frame* 1104 freeframe(Aoedev *d) 1105 { 1106 if(d->nout < d->maxout) 1107 return getframe(d, Tfree); 1108 return nil; 1109 } 1110 1111 static void 1112 work(Aoedev *d) 1113 { 1114 Frame *f; 1115 1116 while ((f = freeframe(d)) != nil) { 1117 if(d->inprocess == nil){ 1118 if(d->head == nil) 1119 return; 1120 d->inprocess = d->head; 1121 d->head = d->head->next; 1122 if(d->head == nil) 1123 d->tail = nil; 1124 } 1125 atarw(d, f); 1126 } 1127 } 1128 1129 static void 1130 strategy(Aoedev *d, Srb *srb) 1131 { 1132 qlock(d); 1133 if(waserror()){ 1134 qunlock(d); 1135 nexterror(); 1136 } 1137 srb->next = nil; 1138 if(d->tail) 1139 d->tail->next = srb; 1140 d->tail = srb; 1141 if(d->head == nil) 1142 d->head = srb; 1143 work(d); 1144 poperror(); 1145 qunlock(d); 1146 1147 while(waserror()) 1148 ; 1149 sleep(srb, srbready, srb); 1150 poperror(); 1151 } 1152 1153 #define iskaddr(a) ((uintptr)(a) > KZERO) 1154 1155 static long 1156 rw(Aoedev *d, int write, uchar *db, long len, uvlong off) 1157 { 1158 long n, nlen, copy; 1159 enum { Srbsz = 1<<19, }; /* magic allocation */ 1160 Srb *srb; 1161 1162 if((off|len) & (Aoesectsz-1)) 1163 error("offset and length must be sector multiple.\n"); 1164 if(off > d->bsize || len == 0) 1165 return 0; 1166 if(off + len > d->bsize) 1167 len = d->bsize - off; 1168 copy = 0; 1169 if(iskaddr(db)){ 1170 srb = srbkalloc(db, len); 1171 copy = 1; 1172 }else 1173 srb = srballoc(Srbsz <= len? Srbsz: len); 1174 if(waserror()){ 1175 srbfree(srb); 1176 nexterror(); 1177 } 1178 nlen = len; 1179 srb->write = write; 1180 do { 1181 if(!UP(d)) 1182 error(Eio); 1183 srb->sector = off / Aoesectsz; 1184 srb->dp = srb->data; 1185 n = nlen; 1186 if(n > Srbsz) 1187 n = Srbsz; 1188 srb->len = n; 1189 if(write && !copy) 1190 memmove(srb->data, db, n); 1191 strategy(d, srb); 1192 if(srb->error) 1193 error(srb->error); 1194 if(!write && !copy) 1195 memmove(db, srb->data, n); 1196 nlen -= n; 1197 db += n; 1198 off += n; 1199 } while (nlen > 0); 1200 poperror(); 1201 srbfree(srb); 1202 return len; 1203 } 1204 1205 static long 1206 readmem(ulong off, void *dst, long n, void *src, long size) 1207 { 1208 if(off >= size) 1209 return 0; 1210 if(off + n > size) 1211 n = size - off; 1212 memmove(dst, (uchar*)src + off, n); 1213 return n; 1214 } 1215 1216 static char * 1217 pflag(char *s, char *e, uchar f) 1218 { 1219 uchar i; 1220 1221 for(i = 0; i < 8; i++) 1222 if(f & (1 << i)) 1223 s = seprint(s, e, "%s ", flagname[i]? flagname[i]: "oops"); 1224 return seprint(s, e, "\n"); 1225 } 1226 1227 static int 1228 pstat(Aoedev *d, char *db, int len, int off) 1229 { 1230 int i; 1231 char *state, *s, *p, *e; 1232 1233 s = p = malloc(READSTR); 1234 if(s == nil) 1235 error(Enomem); 1236 e = p + READSTR; 1237 1238 state = "down"; 1239 if(UP(d)) 1240 state = "up"; 1241 1242 p = seprint(p, e, 1243 "state: %s\n" "nopen: %d\n" "nout: %d\n" 1244 "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d\n" 1245 "fw: %.4ux\n" 1246 "model: %s\n" "serial: %s\n" "firmware: %s\n", 1247 state, d->nopen, d->nout, 1248 d->maxout, d->nframes, d->maxbcnt, 1249 d->fwver, 1250 d->model, d->serial, d->firmware); 1251 p = seprint(p, e, "flag: "); 1252 p = pflag(p, e, d->flag); 1253 1254 if(p - s < len) 1255 len = p - s; 1256 i = readstr(off, db, len, s); 1257 free(s); 1258 return i; 1259 } 1260 1261 static long 1262 unitread(Chan *c, void *db, long len, vlong off) 1263 { 1264 Aoedev *d; 1265 1266 d = unit2dev(UNIT(c->qid)); 1267 if(d->vers != c->qid.vers) 1268 error(Echange); 1269 switch(TYPE(c->qid)){ 1270 default: 1271 error(Ebadarg); 1272 case Qctl: 1273 return pstat(d, db, len, off); 1274 case Qdata: 1275 return rw(d, Read, db, len, off); 1276 case Qconfig: 1277 if (!UP(d)) 1278 error(Enotup); 1279 return readmem(off, db, len, d->config, d->nconfig); 1280 case Qident: 1281 if (!UP(d)) 1282 error(Enotup); 1283 return readmem(off, db, len, d->ident, sizeof d->ident); 1284 } 1285 } 1286 1287 static int 1288 devlinkread(Chan *c, void *db, int len, int off) 1289 { 1290 int i; 1291 char *s, *p, *e; 1292 Aoedev *d; 1293 Devlink *l; 1294 1295 d = unit2dev(UNIT(c->qid)); 1296 i = L(c->qid); 1297 if(i >= d->ndl) 1298 return 0; 1299 l = d->dl + i; 1300 1301 s = p = malloc(READSTR); 1302 if(s == nil) 1303 error(Enomem); 1304 e = s + READSTR; 1305 1306 p = seprint(p, e, "addr: "); 1307 for(i = 0; i < l->nea; i++) 1308 p = seprint(p, e, "%E ", l->eatab[i]); 1309 p = seprint(p, e, "\n"); 1310 p = seprint(p, e, "npkt: %uld\n", l->npkt); 1311 p = seprint(p, e, "resent: %uld\n", l->resent); 1312 p = seprint(p, e, "flag: "); p = pflag(p, e, l->flag); 1313 p = seprint(p, e, "rttavg: %uld\n", TK2MS(l->rttavg)); 1314 p = seprint(p, e, "mintimer: %uld\n", TK2MS(l->mintimer)); 1315 1316 p = seprint(p, e, "nl path: %s\n", l->nl->path); 1317 p = seprint(p, e, "nl ea: %E\n", l->nl->ea); 1318 p = seprint(p, e, "nl flag: "); p = pflag(p, e, l->flag); 1319 p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo); 1320 p = seprint(p, e, "nl datamtu: %d\n", l->nl->datamtu); 1321 1322 if(p - s < len) 1323 len = p - s; 1324 i = readstr(off, db, len, s); 1325 free(s); 1326 return i; 1327 } 1328 1329 static long 1330 topctlread(Chan *, void *db, int len, int off) 1331 { 1332 int i; 1333 char *s, *p, *e; 1334 Netlink *n; 1335 1336 s = p = malloc(READSTR); 1337 if(s == nil) 1338 error(Enomem); 1339 e = s + READSTR; 1340 1341 p = seprint(p, e, "debug: %d\n", debug); 1342 p = seprint(p, e, "autodiscover: %d\n", autodiscover); 1343 p = seprint(p, e, "rediscover: %d\n", rediscover); 1344 1345 for(i = 0; i < Nnetlink; i++){ 1346 n = netlinks.nl+i; 1347 if(n->cc == 0) 1348 continue; 1349 p = seprint(p, e, "if%d path: %s\n", i, n->path); 1350 p = seprint(p, e, "if%d ea: %E\n", i, n->ea); 1351 p = seprint(p, e, "if%d flag: ", i); p = pflag(p, e, n->flag); 1352 p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo); 1353 p = seprint(p, e, "if%d datamtu: %d\n", i, n->datamtu); 1354 } 1355 1356 if(p - s < len) 1357 len = p - s; 1358 i = readstr(off, db, len, s); 1359 free(s); 1360 return i; 1361 } 1362 1363 static long 1364 aoeread(Chan *c, void *db, long n, vlong off) 1365 { 1366 switch(TYPE(c->qid)){ 1367 default: 1368 error(Eperm); 1369 case Qzero: 1370 case Qtopdir: 1371 case Qunitdir: 1372 case Qdevlinkdir: 1373 return devdirread(c, db, n, 0, 0, aoegen); 1374 case Qtopctl: 1375 return topctlread(c, db, n, off); 1376 case Qtoplog: 1377 return eventlogread(db, n); 1378 case Qctl: 1379 case Qdata: 1380 case Qconfig: 1381 case Qident: 1382 return unitread(c, db, n, off); 1383 case Qdevlink: 1384 return devlinkread(c, db, n, off); 1385 } 1386 } 1387 1388 static long 1389 configwrite(Aoedev *d, void *db, long len) 1390 { 1391 char *s; 1392 Aoeqc *ch; 1393 Frame *f; 1394 Srb *srb; 1395 1396 if(!UP(d)) 1397 error(Enotup); 1398 if(len > ETHERMAXTU - AOEQCSZ) 1399 error(Etoobig); 1400 srb = srballoc(len); 1401 s = malloc(len); 1402 if(s == nil) 1403 error(Enomem); 1404 memmove(s, db, len); 1405 if(waserror()){ 1406 srbfree(srb); 1407 free(s); 1408 nexterror(); 1409 } 1410 for (;;) { 1411 qlock(d); 1412 if(waserror()){ 1413 qunlock(d); 1414 nexterror(); 1415 } 1416 f = freeframe(d); 1417 if(f != nil) 1418 break; 1419 poperror(); 1420 qunlock(d); 1421 if(waserror()) 1422 nexterror(); 1423 tsleep(&up->sleep, return0, 0, 100); 1424 poperror(); 1425 } 1426 f->nhdr = AOEQCSZ; 1427 memset(f->hdr, 0, f->nhdr); 1428 ch = (Aoeqc*)f->hdr; 1429 if(hset(d, f, ch, ACconfig) == -1) 1430 return 0; 1431 f->srb = srb; 1432 f->dp = s; 1433 ch->verccmd = AQCfset; 1434 hnputs(ch->cslen, len); 1435 d->nout++; 1436 srb->nout++; 1437 f->dl->npkt++; 1438 f->dlen = len; 1439 /* 1440 * these refer to qlock & waserror in the above for loop. 1441 * there's still the first waserror outstanding. 1442 */ 1443 poperror(); 1444 qunlock(d); 1445 1446 devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); 1447 sleep(srb, srbready, srb); 1448 if(srb->error) 1449 error(srb->error); 1450 1451 qlock(d); 1452 if(waserror()){ 1453 qunlock(d); 1454 nexterror(); 1455 } 1456 memmove(d->config, s, len); 1457 d->nconfig = len; 1458 poperror(); 1459 qunlock(d); 1460 1461 poperror(); /* pop first waserror */ 1462 1463 srbfree(srb); 1464 memmove(db, s, len); 1465 free(s); 1466 return len; 1467 } 1468 1469 static int getmtu(Chan*); 1470 1471 static int 1472 devmaxdata(Aoedev *d) /* return aoe mtu (excluding headers) */ 1473 { 1474 int i, nmtu, mtu; 1475 Devlink *l; 1476 Netlink *n; 1477 1478 mtu = 100000; 1479 for(i = 0; i < d->ndl; i++){ 1480 l = d->dl + i; 1481 n = l->nl; 1482 if((l->flag & Dup) == 0 || (n->flag & Dup) == 0) 1483 continue; 1484 nmtu = getmtu(n->mtu); 1485 if(mtu > nmtu) 1486 mtu = nmtu; 1487 } 1488 if(mtu == 100000) 1489 mtu = ETHERMAXTU; /* normal ethernet mtu */ 1490 mtu -= AOEATASZ; 1491 mtu -= (uint)mtu % Aoesectsz; 1492 if(mtu < 2*Aoesectsz) /* sanity */ 1493 mtu = 2*Aoesectsz; 1494 return mtu; 1495 } 1496 1497 static int 1498 toggle(char *s, int f, int bit) 1499 { 1500 if(s == nil) 1501 f ^= bit; 1502 else if(strcmp(s, "on") == 0) 1503 f |= bit; 1504 else 1505 f &= ~bit; 1506 return f; 1507 } 1508 1509 static void ataident(Aoedev*); 1510 1511 static long 1512 unitctlwrite(Aoedev *d, void *db, long n) 1513 { 1514 uint maxbcnt, mtu; 1515 uvlong bsize; 1516 enum { 1517 Failio, 1518 Ident, 1519 Jumbo, 1520 Maxbno, 1521 Mtu, 1522 Nofailf, 1523 Setsize, 1524 }; 1525 Cmdbuf *cb; 1526 Cmdtab *ct; 1527 static Cmdtab cmds[] = { 1528 {Failio, "failio", 1 }, 1529 {Ident, "identify", 1 }, 1530 {Jumbo, "jumbo", 0 }, 1531 {Maxbno, "maxbno", 0 }, 1532 {Mtu, "mtu", 0 }, 1533 {Nofailf, "nofail", 0 }, 1534 {Setsize, "setsize", 0 }, 1535 }; 1536 1537 cb = parsecmd(db, n); 1538 qlock(d); 1539 if(waserror()){ 1540 qunlock(d); 1541 free(cb); 1542 nexterror(); 1543 } 1544 ct = lookupcmd(cb, cmds, nelem(cmds)); 1545 switch(ct->index){ 1546 case Failio: 1547 downdev(d, "i/o failure"); 1548 break; 1549 case Ident: 1550 ataident(d); 1551 break; 1552 case Jumbo: 1553 d->flag = toggle(cb->f[1], d->flag, Djumbo); 1554 break; 1555 case Maxbno: 1556 case Mtu: 1557 maxbcnt = devmaxdata(d); 1558 if(cb->nf > 2) 1559 error(Ecmdargs); 1560 if(cb->nf == 2){ 1561 mtu = strtoul(cb->f[1], 0, 0); 1562 if(ct->index == Maxbno) 1563 mtu *= Aoesectsz; 1564 else{ 1565 mtu -= AOEATASZ; 1566 mtu &= ~(Aoesectsz-1); 1567 } 1568 if(mtu == 0 || mtu > maxbcnt) 1569 cmderror(cb, "mtu out of legal range"); 1570 maxbcnt = mtu; 1571 } 1572 d->maxbcnt = maxbcnt; 1573 break; 1574 case Nofailf: 1575 d->flag = toggle(cb->f[1], d->flag, Dnofail); 1576 break; 1577 case Setsize: 1578 bsize = d->realbsize; 1579 if(cb->nf > 2) 1580 error(Ecmdargs); 1581 if(cb->nf == 2){ 1582 bsize = strtoull(cb->f[1], 0, 0); 1583 if(bsize % Aoesectsz) 1584 cmderror(cb, "disk size must be sector aligned"); 1585 } 1586 d->bsize = bsize; 1587 break; 1588 default: 1589 cmderror(cb, "unknown aoe control message"); 1590 } 1591 poperror(); 1592 qunlock(d); 1593 free(cb); 1594 return n; 1595 } 1596 1597 static long 1598 unitwrite(Chan *c, void *db, long n, vlong off) 1599 { 1600 long rv; 1601 char *buf; 1602 Aoedev *d; 1603 1604 d = unit2dev(UNIT(c->qid)); 1605 switch(TYPE(c->qid)){ 1606 default: 1607 error(Ebadarg); 1608 case Qctl: 1609 return unitctlwrite(d, db, n); 1610 case Qident: 1611 error(Eperm); 1612 case Qdata: 1613 return rw(d, Write, db, n, off); 1614 case Qconfig: 1615 if(off + n > sizeof d->config) 1616 error(Etoobig); 1617 buf = malloc(sizeof d->config); 1618 if(buf == nil) 1619 error(Enomem); 1620 if(waserror()){ 1621 free(buf); 1622 nexterror(); 1623 } 1624 memmove(buf, d->config, d->nconfig); 1625 memmove(buf + off, db, n); 1626 rv = configwrite(d, buf, n + off); 1627 poperror(); 1628 free(buf); 1629 return rv; 1630 } 1631 } 1632 1633 static Netlink* 1634 addnet(char *path, Chan *cc, Chan *dc, Chan *mtu, uchar *ea) 1635 { 1636 Netlink *nl, *e; 1637 1638 lock(&netlinks); 1639 if(waserror()){ 1640 unlock(&netlinks); 1641 nexterror(); 1642 } 1643 nl = netlinks.nl; 1644 e = nl + nelem(netlinks.nl); 1645 for(; nl < e && nl->cc; nl++) 1646 continue; 1647 if (nl >= e) 1648 error("out of netlink structures"); 1649 nl->cc = cc; 1650 nl->dc = dc; 1651 nl->mtu = mtu; 1652 strncpy(nl->path, path, sizeof nl->path); 1653 memmove(nl->ea, ea, sizeof nl->ea); 1654 poperror(); 1655 nl->flag |= Dup; 1656 unlock(&netlinks); 1657 return nl; 1658 } 1659 1660 static int 1661 newunit(void) 1662 { 1663 int x; 1664 1665 lock(&units); 1666 if(units.ref == Maxunits) 1667 x = -1; 1668 else 1669 x = units.ref++; 1670 unlock(&units); 1671 return x; 1672 } 1673 1674 static int 1675 dropunit(void) 1676 { 1677 int x; 1678 1679 lock(&units); 1680 x = --units.ref; 1681 unlock(&units); 1682 return x; 1683 } 1684 1685 /* 1686 * always allocate max frames. maxout may change. 1687 */ 1688 static Aoedev* 1689 newdev(long major, long minor, int n) 1690 { 1691 Aoedev *d; 1692 Frame *f, *e; 1693 1694 d = mallocz(sizeof *d, 1); 1695 f = mallocz(sizeof *f * Maxframes, 1); 1696 if (!d || !f) { 1697 free(d); 1698 free(f); 1699 error("aoe device allocation failure"); 1700 } 1701 d->nframes = n; 1702 d->frames = f; 1703 for (e = f + n; f < e; f++) 1704 f->tag = Tfree; 1705 d->maxout = n; 1706 d->major = major; 1707 d->minor = minor; 1708 d->maxbcnt = Dbcnt; 1709 d->flag = Djumbo; 1710 d->unit = newunit(); /* bzzt. inaccurate if units removed */ 1711 if(d->unit == -1){ 1712 free(d); 1713 free(d->frames); 1714 error("too many units"); 1715 } 1716 d->dl = d->dltab; 1717 return d; 1718 } 1719 1720 static Aoedev* 1721 mm2dev(int major, int minor) 1722 { 1723 Aoedev *d; 1724 1725 rlock(&devs); 1726 for(d = devs.d; d; d = d->next) 1727 if(d->major == major && d->minor == minor){ 1728 runlock(&devs); 1729 return d; 1730 } 1731 runlock(&devs); 1732 eventlog("mm2dev: %d.%d not found\n", major, minor); 1733 return nil; 1734 } 1735 1736 /* Find the device in our list. If not known, add it */ 1737 static Aoedev* 1738 getdev(long major, long minor, int n) 1739 { 1740 Aoedev *d; 1741 1742 if(major == 0xffff || minor == 0xff) 1743 return 0; 1744 wlock(&devs); 1745 if(waserror()){ 1746 wunlock(&devs); 1747 nexterror(); 1748 } 1749 for(d = devs.d; d; d = d->next) 1750 if(d->major == major && d->minor == minor) 1751 break; 1752 if (d == nil) { 1753 d = newdev(major, minor, n); 1754 d->next = devs.d; 1755 devs.d = d; 1756 } 1757 poperror(); 1758 wunlock(&devs); 1759 return d; 1760 } 1761 1762 static ushort 1763 gbit16(void *a) 1764 { 1765 uchar *i; 1766 1767 i = a; 1768 return i[1] << 8 | i[0]; 1769 } 1770 1771 static ulong 1772 gbit32(void *a) 1773 { 1774 ulong j; 1775 uchar *i; 1776 1777 i = a; 1778 j = i[3] << 24; 1779 j |= i[2] << 16; 1780 j |= i[1] << 8; 1781 j |= i[0]; 1782 return j; 1783 } 1784 1785 static uvlong 1786 gbit64(void *a) 1787 { 1788 uchar *i; 1789 1790 i = a; 1791 return (uvlong)gbit32(i+4) << 32 | gbit32(a); 1792 } 1793 1794 static void 1795 ataident(Aoedev *d) 1796 { 1797 Aoeata *a; 1798 Block *b; 1799 Frame *f; 1800 1801 f = freeframe(d); 1802 if(f == nil) 1803 return; 1804 f->nhdr = AOEATASZ; 1805 memset(f->hdr, 0, f->nhdr); 1806 a = (Aoeata*)f->hdr; 1807 if(hset(d, f, a, ACata) == -1) 1808 return; 1809 a->cmdstat = Cid; /* ata 6, page 110 */ 1810 a->scnt = 1; 1811 a->lba[3] = 0xa0; 1812 d->nout++; 1813 f->dl->npkt++; 1814 f->bcnt = 512; 1815 f->dlen = 0; 1816 b = allocfb(f); 1817 devtab[f->nl->dc->type]->bwrite(f->nl->dc, b, 0); 1818 } 1819 1820 static int 1821 getmtu(Chan *mtuch) 1822 { 1823 int n, mtu; 1824 char buf[36]; 1825 1826 mtu = ETHERMAXTU; 1827 if(mtuch == nil || waserror()) 1828 return mtu; 1829 n = devtab[mtuch->type]->read(mtuch, buf, sizeof buf - 1, 0); 1830 if(n > 12){ 1831 buf[n] = 0; 1832 mtu = strtoul(buf + 12, 0, 0); 1833 } 1834 poperror(); 1835 return mtu; 1836 } 1837 1838 static int 1839 newdlea(Devlink *l, uchar *ea) 1840 { 1841 int i; 1842 uchar *t; 1843 1844 for(i = 0; i < Nea; i++){ 1845 t = l->eatab[i]; 1846 if(i == l->nea){ 1847 memmove(t, ea, Eaddrlen); 1848 return l->nea++; 1849 } 1850 if(memcmp(t, ea, Eaddrlen) == 0) 1851 return i; 1852 } 1853 return -1; 1854 } 1855 1856 static Devlink* 1857 newdevlink(Aoedev *d, Netlink *n, Aoeqc *c) 1858 { 1859 int i; 1860 Devlink *l; 1861 1862 for(i = 0; i < Ndevlink; i++){ 1863 l = d->dl + i; 1864 if(i == d->ndl){ 1865 d->ndl++; 1866 newdlea(l, c->src); 1867 l->nl = n; 1868 l->flag |= Dup; 1869 l->mintimer = Rtmin; 1870 l->rttavg = Rtmax; 1871 return l; 1872 } 1873 if(l->nl == n) { 1874 newdlea(l, c->src); 1875 l->flag |= Dup; 1876 return l; 1877 } 1878 } 1879 eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, c->src); 1880 return 0; 1881 } 1882 1883 static void 1884 errrsp(Block *b, char *s) 1885 { 1886 int n; 1887 Aoedev *d; 1888 Aoehdr *h; 1889 Frame *f; 1890 1891 h = (Aoehdr*)b->rp; 1892 n = nhgetl(h->tag); 1893 if(n == Tmgmt || n == Tfree) 1894 return; 1895 d = mm2dev(nhgets(h->major), h->minor); 1896 if(d == 0) 1897 return; 1898 if(f = getframe(d, n)) 1899 frameerror(d, f, s); 1900 } 1901 1902 static void 1903 qcfgrsp(Block *b, Netlink *nl) 1904 { 1905 int major, cmd, cslen, blen; 1906 unsigned n; 1907 Aoedev *d; 1908 Aoeqc *ch; 1909 Devlink *l; 1910 Frame *f; 1911 1912 ch = (Aoeqc*)b->rp; 1913 major = nhgets(ch->major); 1914 n = nhgetl(ch->tag); 1915 if(n != Tmgmt){ 1916 d = mm2dev(major, ch->minor); 1917 if(d == nil) 1918 return; 1919 qlock(d); 1920 f = getframe(d, n); 1921 if(f == nil){ 1922 qunlock(d); 1923 eventlog("%æ: unknown response tag %ux\n", d, n); 1924 return; 1925 } 1926 cslen = nhgets(ch->cslen); 1927 blen = BLEN(b) - AOEQCSZ; 1928 if(cslen < blen && BLEN(b) > 60) 1929 eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n", 1930 d, n, cslen, blen); 1931 if(cslen > blen){ 1932 eventlog("%æ: cfgrsp: tag %.8ux runt %d %d\n", 1933 d, n, cslen, blen); 1934 cslen = blen; 1935 } 1936 memmove(f->dp, ch + 1, cslen); 1937 f->srb->nout--; 1938 wakeup(f->srb); 1939 d->nout--; 1940 f->srb = nil; 1941 f->tag = Tfree; 1942 qunlock(d); 1943 return; 1944 } 1945 1946 cmd = ch->verccmd & 0xf; 1947 if(cmd != 0){ 1948 eventlog("aoe%d.%d: cfgrsp: bad command %d\n", major, ch->minor, cmd); 1949 return; 1950 } 1951 n = nhgets(ch->bufcnt); 1952 if(n > Maxframes) 1953 n = Maxframes; 1954 1955 if(waserror()){ 1956 eventlog("getdev: %d.%d ignored: %s\n", major, ch->minor, up->errstr); 1957 return; 1958 } 1959 d = getdev(major, ch->minor, n); 1960 poperror(); 1961 if(d == 0) 1962 return; 1963 1964 qlock(d); 1965 *up->errstr = 0; 1966 if(waserror()){ 1967 qunlock(d); 1968 eventlog("%æ: %s\n", d, up->errstr); 1969 nexterror(); 1970 } 1971 1972 l = newdevlink(d, nl, ch); /* add this interface. */ 1973 1974 d->fwver = nhgets(ch->fwver); 1975 n = nhgets(ch->cslen); 1976 if(n > sizeof d->config) 1977 n = sizeof d->config; 1978 d->nconfig = n; 1979 memmove(d->config, ch + 1, n); 1980 if(l != 0 && d->flag & Djumbo){ 1981 n = getmtu(nl->mtu) - AOEATASZ; 1982 n /= Aoesectsz; 1983 if(n > ch->scnt) 1984 n = ch->scnt; 1985 n = n? n * Aoesectsz: Dbcnt; 1986 if(n != d->maxbcnt){ 1987 eventlog("%æ: setting %d byte data frames on %s:%E\n", 1988 d, n, nl->path, nl->ea); 1989 d->maxbcnt = n; 1990 } 1991 } 1992 if(d->nopen == 0) 1993 ataident(d); 1994 poperror(); 1995 qunlock(d); 1996 } 1997 1998 void 1999 aoeidmove(char *p, ushort *u, unsigned n) 2000 { 2001 int i; 2002 char *op, *e, *s; 2003 2004 op = p; 2005 /* 2006 * the ushort `*u' is sometimes not aligned on a short boundary, 2007 * so dereferencing u[i] causes an alignment exception on 2008 * some machines. 2009 */ 2010 s = (char *)u; 2011 for(i = 0; i < n; i += 2){ 2012 *p++ = s[i + 1]; 2013 *p++ = s[i]; 2014 } 2015 *p = 0; 2016 while(p > op && *--p == ' ') 2017 *p = 0; 2018 e = p; 2019 p = op; 2020 while(*p == ' ') 2021 p++; 2022 memmove(op, p, n - (e - p)); 2023 } 2024 2025 static vlong 2026 aoeidentify(Aoedev *d, ushort *id) 2027 { 2028 int i; 2029 vlong s; 2030 2031 d->flag &= ~(Dllba|Dpower|Dsmart|Dnop|Dup); 2032 2033 i = gbit16(id+83) | gbit16(id+86); 2034 if(i & (1<<10)){ 2035 d->flag |= Dllba; 2036 s = gbit64(id+100); 2037 }else 2038 s = gbit32(id+60); 2039 2040 i = gbit16(id+83); 2041 if((i>>14) == 1) { 2042 if(i & (1<<3)) 2043 d->flag |= Dpower; 2044 i = gbit16(id+82); 2045 if(i & 1) 2046 d->flag |= Dsmart; 2047 if(i & (1<<14)) 2048 d->flag |= Dnop; 2049 } 2050 // eventlog("%æ up\n", d); 2051 d->flag |= Dup; 2052 memmove(d->ident, id, sizeof d->ident); 2053 return s; 2054 } 2055 2056 static void 2057 newvers(Aoedev *d) 2058 { 2059 lock(&drivevers); 2060 d->vers = drivevers.ref++; 2061 unlock(&drivevers); 2062 } 2063 2064 static int 2065 identify(Aoedev *d, ushort *id) 2066 { 2067 vlong osectors, s; 2068 uchar oserial[21]; 2069 2070 s = aoeidentify(d, id); 2071 if(s == -1) 2072 return -1; 2073 osectors = d->realbsize; 2074 memmove(oserial, d->serial, sizeof d->serial); 2075 2076 aoeidmove(d->serial, id+10, 20); 2077 aoeidmove(d->firmware, id+23, 8); 2078 aoeidmove(d->model, id+27, 40); 2079 2080 s *= Aoesectsz; 2081 if((osectors == 0 || osectors != s) && 2082 memcmp(oserial, d->serial, sizeof oserial) != 0){ 2083 d->bsize = s; 2084 d->realbsize = s; 2085 // d->mediachange = 1; 2086 newvers(d); 2087 } 2088 return 0; 2089 } 2090 2091 static void 2092 atarsp(Block *b) 2093 { 2094 unsigned n; 2095 short major; 2096 Aoeata *ahin, *ahout; 2097 Aoedev *d; 2098 Frame *f; 2099 Srb *srb; 2100 2101 ahin = (Aoeata*)b->rp; 2102 major = nhgets(ahin->major); 2103 d = mm2dev(major, ahin->minor); 2104 if(d == nil) 2105 return; 2106 qlock(d); 2107 if(waserror()){ 2108 qunlock(d); 2109 nexterror(); 2110 } 2111 n = nhgetl(ahin->tag); 2112 f = getframe(d, n); 2113 if(f == nil){ 2114 dprint("%æ: unexpected response; tag %ux\n", d, n); 2115 goto bail; 2116 } 2117 rtupdate(f->dl, tsince(f->tag)); 2118 ahout = (Aoeata*)f->hdr; 2119 srb = f->srb; 2120 2121 if(ahin->cmdstat & 0xa9){ 2122 eventlog("%æ: ata error cmd %.2ux stat %.2ux\n", 2123 d, ahout->cmdstat, ahin->cmdstat); 2124 if(srb) 2125 srb->error = Eio; 2126 } else { 2127 n = ahout->scnt * Aoesectsz; 2128 switch(ahout->cmdstat){ 2129 case Crd: 2130 case Crdext: 2131 if(BLEN(b) - AOEATASZ < n){ 2132 eventlog("%æ: runt read blen %ld expect %d\n", 2133 d, BLEN(b), n); 2134 goto bail; 2135 } 2136 memmove(f->dp, (uchar *)ahin + AOEATASZ, n); 2137 case Cwr: 2138 case Cwrext: 2139 if(n > Dbcnt) 2140 f->nl->lostjumbo = 0; 2141 if(f->bcnt -= n){ 2142 f->lba += n / Aoesectsz; 2143 f->dp = (uchar*)f->dp + n; 2144 resend(d, f); 2145 goto bail; 2146 } 2147 break; 2148 case Cid: 2149 if(BLEN(b) - AOEATASZ < 512){ 2150 eventlog("%æ: runt identify blen %ld expect %d\n", 2151 d, BLEN(b), n); 2152 goto bail; 2153 } 2154 identify(d, (ushort*)((uchar *)ahin + AOEATASZ)); 2155 break; 2156 default: 2157 eventlog("%æ: unknown ata command %.2ux \n", 2158 d, ahout->cmdstat); 2159 } 2160 } 2161 2162 if(srb && --srb->nout == 0 && srb->len == 0) 2163 wakeup(srb); 2164 f->srb = nil; 2165 f->tag = Tfree; 2166 d->nout--; 2167 2168 work(d); 2169 bail: 2170 poperror(); 2171 qunlock(d); 2172 } 2173 2174 static void 2175 netrdaoeproc(void *v) 2176 { 2177 int idx; 2178 char name[Maxpath+1], *s; 2179 Aoehdr *h; 2180 Block *b; 2181 Netlink *nl; 2182 2183 nl = (Netlink*)v; 2184 idx = nl - netlinks.nl; 2185 netlinks.reader[idx] = 1; 2186 kstrcpy(name, nl->path, Maxpath); 2187 2188 if(waserror()){ 2189 eventlog("netrdaoe exiting: %s\n", up->errstr); 2190 netlinks.reader[idx] = 0; 2191 wakeup(netlinks.rendez + idx); 2192 pexit(up->errstr, 1); 2193 } 2194 if(autodiscover) 2195 discover(0xffff, 0xff); 2196 for (;;) { 2197 if(!(nl->flag & Dup)) { 2198 uprint("%s: netlink is down", name); 2199 error(up->genbuf); 2200 } 2201 if (nl->dc == nil) 2202 panic("netrdaoe: nl->dc == nil"); 2203 b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0); 2204 if(b == nil) { 2205 uprint("%s: nil read from network", name); 2206 error(up->genbuf); 2207 } 2208 h = (Aoehdr*)b->rp; 2209 if(h->verflag & AFrsp) 2210 if(s = aoeerror(h)){ 2211 eventlog("%s: %s\n", nl->path, up->errstr); 2212 errrsp(b, s); 2213 }else 2214 switch(h->cmd){ 2215 case ACata: 2216 atarsp(b); 2217 break; 2218 case ACconfig: 2219 qcfgrsp(b, nl); 2220 break; 2221 default: 2222 if((h->cmd & 0xf0) == 0){ 2223 eventlog("%s: unknown cmd %d\n", 2224 nl->path, h->cmd); 2225 errrsp(b, "unknown command"); 2226 } 2227 break; 2228 } 2229 freeb(b); 2230 } 2231 } 2232 2233 static void 2234 getaddr(char *path, uchar *ea) 2235 { 2236 int n; 2237 char buf[2*Eaddrlen+1]; 2238 Chan *c; 2239 2240 uprint("%s/addr", path); 2241 c = namec(up->genbuf, Aopen, OREAD, 0); 2242 if(waserror()) { 2243 cclose(c); 2244 nexterror(); 2245 } 2246 if (c == nil) 2247 panic("æ: getaddr: c == nil"); 2248 n = devtab[c->type]->read(c, buf, sizeof buf-1, 0); 2249 poperror(); 2250 cclose(c); 2251 buf[n] = 0; 2252 if(parseether(ea, buf) < 0) 2253 error("parseether failure"); 2254 } 2255 2256 static void 2257 netbind(char *path) 2258 { 2259 char addr[Maxpath]; 2260 uchar ea[2*Eaddrlen+1]; 2261 Chan *dc, *cc, *mtu; 2262 Netlink *nl; 2263 2264 snprint(addr, sizeof addr, "%s!%#x", path, Aoetype); 2265 dc = chandial(addr, nil, nil, &cc); 2266 snprint(addr, sizeof addr, "%s/mtu", path); 2267 if(waserror()) 2268 mtu = nil; 2269 else { 2270 mtu = namec(addr, Aopen, OREAD, 0); 2271 poperror(); 2272 } 2273 2274 if(waserror()){ 2275 cclose(dc); 2276 cclose(cc); 2277 if(mtu) 2278 cclose(mtu); 2279 nexterror(); 2280 } 2281 if(dc == nil || cc == nil) 2282 error(Enonexist); 2283 getaddr(path, ea); 2284 nl = addnet(path, cc, dc, mtu, ea); 2285 snprint(addr, sizeof addr, "netrdaoe@%s", path); 2286 kproc(addr, netrdaoeproc, nl); 2287 poperror(); 2288 } 2289 2290 static int 2291 unbound(void *v) 2292 { 2293 return *(int*)v != 0; 2294 } 2295 2296 static void 2297 netunbind(char *path) 2298 { 2299 int i, idx; 2300 Aoedev *d, *p, *next; 2301 Chan *dc, *cc; 2302 Devlink *l; 2303 Frame *f; 2304 Netlink *n, *e; 2305 2306 n = netlinks.nl; 2307 e = n + nelem(netlinks.nl); 2308 2309 lock(&netlinks); 2310 for(; n < e; n++) 2311 if(n->dc && strcmp(n->path, path) == 0) 2312 break; 2313 unlock(&netlinks); 2314 if (n >= e) 2315 error("device not bound"); 2316 2317 /* 2318 * hunt down devices using this interface; disable 2319 * this also terminates the reader. 2320 */ 2321 idx = n - netlinks.nl; 2322 wlock(&devs); 2323 for(d = devs.d; d; d = d->next){ 2324 qlock(d); 2325 for(i = 0; i < d->ndl; i++){ 2326 l = d->dl + i; 2327 if(l->nl == n) 2328 l->flag &= ~Dup; 2329 } 2330 qunlock(d); 2331 } 2332 n->flag &= ~Dup; 2333 wunlock(&devs); 2334 2335 /* confirm reader is down. */ 2336 while(waserror()) 2337 ; 2338 sleep(netlinks.rendez + idx, unbound, netlinks.reader + idx); 2339 poperror(); 2340 2341 /* reschedule packets. */ 2342 wlock(&devs); 2343 for(d = devs.d; d; d = d->next){ 2344 qlock(d); 2345 for(i = 0; i < d->nframes; i++){ 2346 f = d->frames + i; 2347 if(f->tag != Tfree && f->nl == n) 2348 resend(d, f); 2349 } 2350 qunlock(d); 2351 } 2352 wunlock(&devs); 2353 2354 /* squeeze devlink pool. (we assert nobody is using them now) */ 2355 wlock(&devs); 2356 for(d = devs.d; d; d = d->next){ 2357 qlock(d); 2358 for(i = 0; i < d->ndl; i++){ 2359 l = d->dl + i; 2360 if(l->nl == n) 2361 memmove(l, l + 1, sizeof *l * (--d->ndl - i)); 2362 } 2363 qunlock(d); 2364 } 2365 wunlock(&devs); 2366 2367 /* close device link. */ 2368 lock(&netlinks); 2369 dc = n->dc; 2370 cc = n->cc; 2371 if(n->mtu) 2372 cclose(n->mtu); 2373 memset(n, 0, sizeof *n); 2374 unlock(&netlinks); 2375 2376 cclose(dc); 2377 cclose(cc); 2378 2379 /* squeeze orphan devices */ 2380 wlock(&devs); 2381 for(p = d = devs.d; d; d = next){ 2382 next = d->next; 2383 if(d->ndl > 0) { 2384 p = d; 2385 continue; 2386 } 2387 qlock(d); 2388 downdev(d, "orphan"); 2389 qunlock(d); 2390 if(p != devs.d) 2391 p->next = next; 2392 else{ 2393 devs.d = next; 2394 p = devs.d; 2395 } 2396 free(d->frames); 2397 free(d); 2398 dropunit(); 2399 } 2400 wunlock(&devs); 2401 } 2402 2403 static void 2404 removeaoedev(Aoedev *d) 2405 { 2406 int i; 2407 Aoedev *p; 2408 2409 wlock(&devs); 2410 p = 0; 2411 if(d != devs.d) 2412 for(p = devs.d; p; p = p->next) 2413 if(p->next == d) 2414 break; 2415 qlock(d); 2416 d->flag &= ~Dup; 2417 newvers(d); 2418 d->ndl = 0; 2419 qunlock(d); 2420 for(i = 0; i < d->nframes; i++) 2421 frameerror(d, d->frames+i, Enotup); 2422 2423 if(p) 2424 p->next = d->next; 2425 else 2426 devs.d = d->next; 2427 free(d->frames); 2428 free(d); 2429 dropunit(); 2430 wunlock(&devs); 2431 } 2432 2433 static void 2434 removedev(char *name) 2435 { 2436 Aoedev *d, *p; 2437 2438 wlock(&devs); 2439 for(p = d = devs.d; d; p = d, d = d->next) 2440 if(strcmp(name, unitname(d)) == 0) { 2441 wunlock(&devs); 2442 removeaoedev(p); 2443 return; 2444 } 2445 wunlock(&devs); 2446 error("device not bound"); 2447 } 2448 2449 static void 2450 discoverstr(char *f) 2451 { 2452 ushort shelf, slot; 2453 ulong sh; 2454 char *s; 2455 2456 if(f == 0){ 2457 discover(0xffff, 0xff); 2458 return; 2459 } 2460 2461 shelf = sh = strtol(f, &s, 0); 2462 if(s == f || sh > 0xffff) 2463 error("bad shelf"); 2464 f = s; 2465 if(*f++ == '.'){ 2466 slot = strtol(f, &s, 0); 2467 if(s == f || slot > 0xff) 2468 error("bad shelf"); 2469 }else 2470 slot = 0xff; 2471 discover(shelf, slot); 2472 } 2473 2474 2475 static void 2476 aoeremove(Chan *c) 2477 { 2478 switch(TYPE(c->qid)){ 2479 default: 2480 error(Eperm); 2481 case Qunitdir: 2482 removeaoedev(unit2dev(UNIT(c->qid))); 2483 break; 2484 } 2485 } 2486 2487 static long 2488 topctlwrite(void *db, long n) 2489 { 2490 enum { 2491 Autodiscover, 2492 Bind, 2493 Debug, 2494 Discover, 2495 Rediscover, 2496 Remove, 2497 Unbind, 2498 }; 2499 char *f; 2500 Cmdbuf *cb; 2501 Cmdtab *ct; 2502 static Cmdtab cmds[] = { 2503 { Autodiscover, "autodiscover", 0 }, 2504 { Bind, "bind", 2 }, 2505 { Debug, "debug", 0 }, 2506 { Discover, "discover", 0 }, 2507 { Rediscover, "rediscover", 0 }, 2508 { Remove, "remove", 2 }, 2509 { Unbind, "unbind", 2 }, 2510 }; 2511 2512 cb = parsecmd(db, n); 2513 if(waserror()){ 2514 free(cb); 2515 nexterror(); 2516 } 2517 ct = lookupcmd(cb, cmds, nelem(cmds)); 2518 f = cb->f[1]; 2519 switch(ct->index){ 2520 case Autodiscover: 2521 autodiscover = toggle(f, autodiscover, 1); 2522 break; 2523 case Bind: 2524 netbind(f); 2525 break; 2526 case Debug: 2527 debug = toggle(f, debug, 1); 2528 break; 2529 case Discover: 2530 discoverstr(f); 2531 break; 2532 case Rediscover: 2533 rediscover = toggle(f, rediscover, 1); 2534 break; 2535 case Remove: 2536 removedev(f); 2537 break; 2538 case Unbind: 2539 netunbind(f); 2540 break; 2541 default: 2542 cmderror(cb, "unknown aoe control message"); 2543 } 2544 poperror(); 2545 free(cb); 2546 return n; 2547 } 2548 2549 static long 2550 aoewrite(Chan *c, void *db, long n, vlong off) 2551 { 2552 switch(TYPE(c->qid)){ 2553 default: 2554 case Qzero: 2555 case Qtopdir: 2556 case Qunitdir: 2557 case Qtoplog: 2558 error(Eperm); 2559 case Qtopctl: 2560 return topctlwrite(db, n); 2561 case Qctl: 2562 case Qdata: 2563 case Qconfig: 2564 case Qident: 2565 return unitwrite(c, db, n, off); 2566 } 2567 } 2568 2569 Dev aoedevtab = { 2570 L'æ', 2571 "aoe", 2572 2573 devreset, 2574 devinit, 2575 devshutdown, 2576 aoeattach, 2577 aoewalk, 2578 aoestat, 2579 aoeopen, 2580 devcreate, 2581 aoeclose, 2582 aoeread, 2583 devbread, 2584 aoewrite, 2585 devbwrite, 2586 aoeremove, 2587 devwstat, 2588 devpower, 2589 devconfig, 2590 }; 2591