devloopback.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. #include "u.h"
  2. #include "../port/lib.h"
  3. #include "mem.h"
  4. #include "dat.h"
  5. #include "fns.h"
  6. #include "../port/error.h"
  7. typedef struct Link Link;
  8. typedef struct Loop Loop;
  9. struct Link
  10. {
  11. Lock;
  12. int ref;
  13. long packets; /* total number of packets sent */
  14. long bytes; /* total number of bytes sent */
  15. int indrop; /* enable dropping on iq overflow */
  16. long soverflows; /* packets dropped because iq overflowed */
  17. long droprate; /* drop 1/droprate packets in tq */
  18. long drops; /* packets deliberately dropped */
  19. vlong delay0ns; /* nanosec of delay in the link */
  20. long delaynns; /* nanosec of delay per byte */
  21. vlong delay0; /* fastticks of delay */
  22. long delayn;
  23. Block *tq; /* transmission queue */
  24. Block *tqtail;
  25. vlong tout; /* time the last packet in tq is really out */
  26. vlong tin; /* time the head packet in tq enters the remote side */
  27. long limit; /* queue buffering limit */
  28. Queue *oq; /* output queue from other side & packets in the link */
  29. Queue *iq;
  30. Timer ci; /* time to move packets from next packet from oq */
  31. };
  32. struct Loop
  33. {
  34. QLock;
  35. int ref;
  36. int minmtu; /* smallest block transmittable */
  37. Loop *next;
  38. ulong path;
  39. Link link[2];
  40. };
  41. static struct
  42. {
  43. Lock;
  44. ulong path;
  45. } loopbackalloc;
  46. enum
  47. {
  48. Qtopdir= 1, /* top level directory */
  49. Qloopdir, /* loopback* directory */
  50. Qportdir, /* directory each end of the loop */
  51. Qctl,
  52. Qstatus,
  53. Qstats,
  54. Qdata,
  55. MaxQ,
  56. Nloopbacks = 5,
  57. Statelen = 23*1024, /* status buffer size */
  58. Tmsize = 8,
  59. Delayn = 10000, /* default delays */
  60. Delay0 = 2500000,
  61. Loopqlim = 32*1024, /* default size of queues */
  62. };
  63. static Dirtab loopportdir[] =
  64. {
  65. "ctl", {Qctl}, 0, 0222,
  66. "status", {Qstatus}, 0, 0444,
  67. "stats", {Qstats}, 0, 0444,
  68. "data", {Qdata}, 0, 0666,
  69. };
  70. static Dirtab loopdirs[MaxQ];
  71. static Loop loopbacks[Nloopbacks];
  72. static uvlong fasthz;
  73. #define TYPE(x) (((ulong)(x))&0xff)
  74. #define ID(x) (((ulong)(x))>>8)
  75. #define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y)))
  76. #define NS2FASTHZ(t) ((fasthz*(t))/1000000000);
  77. static void looper(Loop *lb);
  78. static long loopoput(Loop *lb, Link *link, Block *bp);
  79. static void ptime(uchar *p, vlong t);
  80. static vlong gtime(uchar *p);
  81. static void closelink(Link *link, int dofree);
  82. static void pushlink(Link *link, vlong now);
  83. static void freelb(Loop *lb);
  84. static void linkintr(Ureg*, Timer *ci);
  85. static void
  86. loopbackinit(void)
  87. {
  88. int i;
  89. for(i = 0; i < Nloopbacks; i++)
  90. loopbacks[i].path = i;
  91. /* invert directory tables for non-directory entries */
  92. for(i=0; i<nelem(loopportdir); i++)
  93. loopdirs[loopportdir[i].qid.path] = loopportdir[i];
  94. }
  95. static Chan*
  96. loopbackattach(char *spec)
  97. {
  98. Loop *volatile lb;
  99. Queue *q;
  100. Chan *c;
  101. int chan;
  102. int dev;
  103. dev = 0;
  104. if(spec != nil){
  105. dev = atoi(spec);
  106. if(dev >= Nloopbacks)
  107. error(Ebadspec);
  108. }
  109. c = devattach('X', spec);
  110. lb = &loopbacks[dev];
  111. qlock(lb);
  112. if(waserror()){
  113. lb->ref--;
  114. qunlock(lb);
  115. nexterror();
  116. }
  117. lb->ref++;
  118. if(lb->ref == 1){
  119. fastticks(&fasthz);
  120. for(chan = 0; chan < 2; chan++){
  121. lb->link[chan].ci.a = &lb->link[chan];
  122. lb->link[chan].ci.f = linkintr;
  123. lb->link[chan].limit = Loopqlim;
  124. q = qopen(lb->link[chan].limit, 0, 0, 0);
  125. lb->link[chan].iq = q;
  126. if(q == nil){
  127. freelb(lb);
  128. exhausted("memory");
  129. }
  130. q = qopen(lb->link[chan].limit, 0, 0, 0);
  131. lb->link[chan].oq = q;
  132. if(q == nil){
  133. freelb(lb);
  134. exhausted("memory");
  135. }
  136. lb->link[chan].indrop = 1;
  137. lb->link[chan].delayn = NS2FASTHZ(Delayn);
  138. lb->link[chan].delaynns = Delayn;
  139. lb->link[chan].delay0 = NS2FASTHZ(Delay0);
  140. lb->link[chan].delay0ns = Delay0;
  141. }
  142. }
  143. poperror();
  144. qunlock(lb);
  145. mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
  146. c->aux = lb;
  147. c->dev = dev;
  148. return c;
  149. }
  150. static int
  151. loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
  152. {
  153. Loop *lb;
  154. Dirtab *tab;
  155. int len, type;
  156. Qid qid;
  157. type = TYPE(c->qid.path);
  158. if(i == DEVDOTDOT){
  159. switch(type){
  160. case Qtopdir:
  161. case Qloopdir:
  162. snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev);
  163. mkqid(&qid, QID(0, Qtopdir), 0, QTDIR);
  164. devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
  165. break;
  166. case Qportdir:
  167. snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
  168. mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
  169. devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
  170. break;
  171. default:
  172. panic("loopbackgen %llux", c->qid.path);
  173. }
  174. return 1;
  175. }
  176. switch(type){
  177. case Qtopdir:
  178. if(i != 0)
  179. return -1;
  180. snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
  181. mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
  182. devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
  183. return 1;
  184. case Qloopdir:
  185. if(i >= 2)
  186. return -1;
  187. snprint(up->genbuf, sizeof(up->genbuf), "%d", i);
  188. mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR);
  189. devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
  190. return 1;
  191. case Qportdir:
  192. if(i >= nelem(loopportdir))
  193. return -1;
  194. tab = &loopportdir[i];
  195. mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE);
  196. devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
  197. return 1;
  198. default:
  199. /* non directory entries end up here; must be in lowest level */
  200. if(c->qid.type & QTDIR)
  201. panic("loopbackgen: unexpected directory");
  202. if(i != 0)
  203. return -1;
  204. tab = &loopdirs[type];
  205. if(tab == nil)
  206. panic("loopbackgen: unknown type: %d", type);
  207. len = tab->length;
  208. if(type == Qdata){
  209. lb = c->aux;
  210. len = qlen(lb->link[ID(c->qid.path)].iq);
  211. }
  212. devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
  213. return 1;
  214. }
  215. }
  216. static Walkqid*
  217. loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
  218. {
  219. Walkqid *wq;
  220. Loop *lb;
  221. wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
  222. if(wq != nil && wq->clone != nil && wq->clone != c){
  223. lb = c->aux;
  224. qlock(lb);
  225. lb->ref++;
  226. if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
  227. lb->link[ID(c->qid.path)].ref++;
  228. qunlock(lb);
  229. }
  230. return wq;
  231. }
  232. static int
  233. loopbackstat(Chan *c, uchar *db, int n)
  234. {
  235. return devstat(c, db, n, nil, 0, loopbackgen);
  236. }
  237. /*
  238. * if the stream doesn't exist, create it
  239. */
  240. static Chan*
  241. loopbackopen(Chan *c, int omode)
  242. {
  243. Loop *lb;
  244. if(c->qid.type & QTDIR){
  245. if(omode != OREAD)
  246. error(Ebadarg);
  247. c->mode = omode;
  248. c->flag |= COPEN;
  249. c->offset = 0;
  250. return c;
  251. }
  252. lb = c->aux;
  253. qlock(lb);
  254. if(TYPE(c->qid.path) == Qdata){
  255. if(lb->link[ID(c->qid.path)].ref){
  256. qunlock(lb);
  257. error(Einuse);
  258. }
  259. lb->link[ID(c->qid.path)].ref++;
  260. }
  261. qunlock(lb);
  262. c->mode = openmode(omode);
  263. c->flag |= COPEN;
  264. c->offset = 0;
  265. c->iounit = qiomaxatomic;
  266. return c;
  267. }
  268. static void
  269. loopbackclose(Chan *c)
  270. {
  271. Loop *lb;
  272. int ref, chan;
  273. lb = c->aux;
  274. qlock(lb);
  275. /*
  276. * closing either side hangs up the stream
  277. */
  278. if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){
  279. chan = ID(c->qid.path);
  280. if(--lb->link[chan].ref == 0){
  281. qhangup(lb->link[chan ^ 1].oq, nil);
  282. looper(lb);
  283. }
  284. }
  285. /*
  286. * if both sides are closed, they are reusable
  287. */
  288. if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
  289. for(chan = 0; chan < 2; chan++){
  290. closelink(&lb->link[chan], 0);
  291. qreopen(lb->link[chan].iq);
  292. qreopen(lb->link[chan].oq);
  293. qsetlimit(lb->link[chan].oq, lb->link[chan].limit);
  294. qsetlimit(lb->link[chan].iq, lb->link[chan].limit);
  295. }
  296. }
  297. ref = --lb->ref;
  298. if(ref == 0)
  299. freelb(lb);
  300. qunlock(lb);
  301. }
  302. static void
  303. freelb(Loop *lb)
  304. {
  305. int chan;
  306. for(chan = 0; chan < 2; chan++)
  307. closelink(&lb->link[chan], 1);
  308. }
  309. /*
  310. * called with the Loop qlocked,
  311. * so only pushlink can mess with the queues
  312. */
  313. static void
  314. closelink(Link *link, int dofree)
  315. {
  316. Queue *iq, *oq;
  317. Block *bp;
  318. ilock(link);
  319. iq = link->iq;
  320. oq = link->oq;
  321. bp = link->tq;
  322. link->tq = nil;
  323. link->tqtail = nil;
  324. link->tout = 0;
  325. link->tin = 0;
  326. timerdel(&link->ci);
  327. iunlock(link);
  328. if(iq != nil){
  329. qclose(iq);
  330. if(dofree){
  331. ilock(link);
  332. free(iq);
  333. link->iq = nil;
  334. iunlock(link);
  335. }
  336. }
  337. if(oq != nil){
  338. qclose(oq);
  339. if(dofree){
  340. ilock(link);
  341. free(oq);
  342. link->oq = nil;
  343. iunlock(link);
  344. }
  345. }
  346. freeblist(bp);
  347. }
  348. static long
  349. loopbackread(Chan *c, void *va, long n, vlong offset)
  350. {
  351. Loop *lb;
  352. Link *link;
  353. char *buf;
  354. long rv;
  355. lb = c->aux;
  356. switch(TYPE(c->qid.path)){
  357. default:
  358. error(Eperm);
  359. return -1; /* not reached */
  360. case Qtopdir:
  361. case Qloopdir:
  362. case Qportdir:
  363. return devdirread(c, va, n, nil, 0, loopbackgen);
  364. case Qdata:
  365. return qread(lb->link[ID(c->qid.path)].iq, va, n);
  366. case Qstatus:
  367. link = &lb->link[ID(c->qid.path)];
  368. buf = smalloc(Statelen);
  369. rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns);
  370. rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit);
  371. rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop);
  372. snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate);
  373. rv = readstr(offset, va, n, buf);
  374. free(buf);
  375. break;
  376. case Qstats:
  377. link = &lb->link[ID(c->qid.path)];
  378. buf = smalloc(Statelen);
  379. rv = snprint(buf, Statelen, "packets: %ld\n", link->packets);
  380. rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes);
  381. rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops);
  382. snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows);
  383. rv = readstr(offset, va, n, buf);
  384. free(buf);
  385. break;
  386. }
  387. return rv;
  388. }
  389. static Block*
  390. loopbackbread(Chan *c, long n, ulong offset)
  391. {
  392. Loop *lb;
  393. lb = c->aux;
  394. if(TYPE(c->qid.path) == Qdata)
  395. return qbread(lb->link[ID(c->qid.path)].iq, n);
  396. return devbread(c, n, offset);
  397. }
  398. static long
  399. loopbackbwrite(Chan *c, Block *bp, ulong off)
  400. {
  401. Loop *lb;
  402. lb = c->aux;
  403. if(TYPE(c->qid.path) == Qdata)
  404. return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp);
  405. return devbwrite(c, bp, off);
  406. }
  407. static long
  408. loopbackwrite(Chan *c, void *va, long n, vlong off)
  409. {
  410. Loop *lb;
  411. Link *link;
  412. Cmdbuf *volatile cb;
  413. Block *volatile bp;
  414. vlong d0, d0ns;
  415. long dn, dnns;
  416. switch(TYPE(c->qid.path)){
  417. case Qdata:
  418. bp = allocb(n);
  419. if(waserror()){
  420. freeb(bp);
  421. nexterror();
  422. }
  423. memmove(bp->wp, va, n);
  424. poperror();
  425. bp->wp += n;
  426. return loopbackbwrite(c, bp, off);
  427. case Qctl:
  428. lb = c->aux;
  429. link = &lb->link[ID(c->qid.path)];
  430. cb = parsecmd(va, n);
  431. if(waserror()){
  432. free(cb);
  433. nexterror();
  434. }
  435. if(cb->nf < 1)
  436. error("short control request");
  437. if(strcmp(cb->f[0], "delay") == 0){
  438. if(cb->nf != 3)
  439. error("usage: delay latency bytedelay");
  440. d0ns = strtoll(cb->f[1], nil, 10);
  441. dnns = strtol(cb->f[2], nil, 10);
  442. /*
  443. * it takes about 20000 cycles on a pentium ii
  444. * to run pushlink; perhaps this should be accounted.
  445. */
  446. d0 = NS2FASTHZ(d0ns);
  447. dn = NS2FASTHZ(dnns);
  448. ilock(link);
  449. link->delay0 = d0;
  450. link->delayn = dn;
  451. link->delay0ns = d0ns;
  452. link->delaynns = dnns;
  453. iunlock(link);
  454. }else if(strcmp(cb->f[0], "indrop") == 0){
  455. if(cb->nf != 2)
  456. error("usage: indrop [01]");
  457. ilock(link);
  458. link->indrop = strtol(cb->f[1], nil, 0) != 0;
  459. iunlock(link);
  460. }else if(strcmp(cb->f[0], "droprate") == 0){
  461. if(cb->nf != 2)
  462. error("usage: droprate ofn");
  463. ilock(link);
  464. link->droprate = strtol(cb->f[1], nil, 0);
  465. iunlock(link);
  466. }else if(strcmp(cb->f[0], "limit") == 0){
  467. if(cb->nf != 2)
  468. error("usage: limit maxqsize");
  469. ilock(link);
  470. link->limit = strtol(cb->f[1], nil, 0);
  471. qsetlimit(link->oq, link->limit);
  472. qsetlimit(link->iq, link->limit);
  473. iunlock(link);
  474. }else if(strcmp(cb->f[0], "reset") == 0){
  475. if(cb->nf != 1)
  476. error("usage: reset");
  477. ilock(link);
  478. link->packets = 0;
  479. link->bytes = 0;
  480. link->indrop = 0;
  481. link->soverflows = 0;
  482. link->drops = 0;
  483. iunlock(link);
  484. }else
  485. error("unknown control request");
  486. poperror();
  487. free(cb);
  488. break;
  489. default:
  490. error(Eperm);
  491. }
  492. return n;
  493. }
  494. static long
  495. loopoput(Loop *lb, Link *link, Block *volatile bp)
  496. {
  497. long n;
  498. n = BLEN(bp);
  499. /* make it a single block with space for the loopback timing header */
  500. if(waserror()){
  501. freeb(bp);
  502. nexterror();
  503. }
  504. bp = padblock(bp, Tmsize);
  505. if(bp->next)
  506. bp = concatblock(bp);
  507. if(BLEN(bp) < lb->minmtu)
  508. bp = adjustblock(bp, lb->minmtu);
  509. poperror();
  510. ptime(bp->rp, fastticks(nil));
  511. link->packets++;
  512. link->bytes += n;
  513. qbwrite(link->oq, bp);
  514. looper(lb);
  515. return n;
  516. }
  517. static void
  518. looper(Loop *lb)
  519. {
  520. vlong t;
  521. int chan;
  522. t = fastticks(nil);
  523. for(chan = 0; chan < 2; chan++)
  524. pushlink(&lb->link[chan], t);
  525. }
  526. static void
  527. linkintr(Ureg*, Timer *ci)
  528. {
  529. Link *link;
  530. link = ci->a;
  531. pushlink(link, ci->when);
  532. }
  533. /*
  534. * move blocks between queues if they are ready.
  535. * schedule an interrupt for the next interesting time.
  536. *
  537. * must be called with the link ilocked.
  538. */
  539. static void
  540. pushlink(Link *link, vlong now)
  541. {
  542. Block *bp;
  543. vlong tout, tin;
  544. /*
  545. * put another block in the link queue
  546. */
  547. ilock(link);
  548. if(link->iq == nil || link->oq == nil){
  549. iunlock(link);
  550. return;
  551. }
  552. timerdel(&link->ci);
  553. /*
  554. * put more blocks into the xmit queue
  555. * use the time the last packet was supposed to go out
  556. * as the start time for the next packet, rather than
  557. * the current time. this more closely models a network
  558. * device which can queue multiple output packets.
  559. */
  560. tout = link->tout;
  561. if(!tout)
  562. tout = now;
  563. while(tout <= now){
  564. bp = qget(link->oq);
  565. if(bp == nil){
  566. tout = 0;
  567. break;
  568. }
  569. /*
  570. * can't send the packet before it gets queued
  571. */
  572. tin = gtime(bp->rp);
  573. if(tin > tout)
  574. tout = tin;
  575. tout = tout + (BLEN(bp) - Tmsize) * link->delayn;
  576. /*
  577. * drop packets
  578. */
  579. if(link->droprate && nrand(link->droprate) == 0)
  580. link->drops++;
  581. else{
  582. ptime(bp->rp, tout + link->delay0);
  583. if(link->tq == nil)
  584. link->tq = bp;
  585. else
  586. link->tqtail->next = bp;
  587. link->tqtail = bp;
  588. }
  589. }
  590. /*
  591. * record the next time a packet can be sent,
  592. * but don't schedule an interrupt if none is waiting
  593. */
  594. link->tout = tout;
  595. if(!qcanread(link->oq))
  596. tout = 0;
  597. /*
  598. * put more blocks into the receive queue
  599. */
  600. tin = 0;
  601. while(bp = link->tq){
  602. tin = gtime(bp->rp);
  603. if(tin > now)
  604. break;
  605. bp->rp += Tmsize;
  606. link->tq = bp->next;
  607. bp->next = nil;
  608. if(!link->indrop)
  609. qpassnolim(link->iq, bp);
  610. else if(qpass(link->iq, bp) < 0)
  611. link->soverflows++;
  612. tin = 0;
  613. }
  614. if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
  615. qhangup(link->iq, nil);
  616. link->tin = tin;
  617. if(!tin || tin > tout && tout)
  618. tin = tout;
  619. link->ci.when = tin;
  620. if(tin){
  621. if(tin < now)
  622. panic("loopback unfinished business");
  623. timeradd(&link->ci);
  624. }
  625. iunlock(link);
  626. }
  627. static void
  628. ptime(uchar *p, vlong t)
  629. {
  630. ulong tt;
  631. tt = t >> 32;
  632. p[0] = tt >> 24;
  633. p[1] = tt >> 16;
  634. p[2] = tt >> 8;
  635. p[3] = tt;
  636. tt = t;
  637. p[4] = tt >> 24;
  638. p[5] = tt >> 16;
  639. p[6] = tt >> 8;
  640. p[7] = tt;
  641. }
  642. static vlong
  643. gtime(uchar *p)
  644. {
  645. ulong t1, t2;
  646. t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
  647. t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
  648. return ((vlong)t1 << 32) | t2;
  649. }
  650. Dev loopbackdevtab = {
  651. 'X',
  652. "loopback",
  653. devreset,
  654. loopbackinit,
  655. devshutdown,
  656. loopbackattach,
  657. loopbackwalk,
  658. loopbackstat,
  659. loopbackopen,
  660. devcreate,
  661. loopbackclose,
  662. loopbackread,
  663. loopbackbread,
  664. loopbackwrite,
  665. loopbackbwrite,
  666. devremove,
  667. devwstat,
  668. };