From mboxrd@z Thu Jan 1 00:00:00 1970 Date: Mon, 3 Nov 1997 01:57:50 -0600 From: G. David Butler gdb@dbSystems.com Subject: [9fans] convS2M improvement (plus a localhost interface) Topicbox-Message-UUID: 6b3a233e-eac8-11e9-9e20-41e7f4b1d025 Message-ID: <19971103075750.r9bGO9t6vMQqo1ZNZtpOiZA741YFVulzsxa5quVFBhA@z> From: beto@ncube.com >Hi, > >I've modified convS2M to avoid having to copy the >data during a read reply. The idea is to set the Fcall data >pointer to point to the right place within the >reply message. You saw that in /sys/src/fs/port/fcall.c too! Don't forget the change in Twrite also. >The server needs to know the data offset for a read reply, >but some macro definitions will solve the problem. > > p->reply.data = (char *) p->rbp->wptr+8; > >With this enhancement a 9P server does not have to copy >data for reads. And for writes, the number is 16. Remember, inside the cpu kernel everything is done with function calls and streams. Conv[SM]2[MS] are only used in authentication and the mnt driver. Having said that, a big problem is devmnt. Because of the need for the header and the atomic writes of 9P, the data has to be copied from user space to form the RPC message for writes and there is no way to map it into the RPC message for reads. I think this is true for any user of conv[SM]2[MS]. [In the file server, there is no user space. Messages are copied to/from the buffer cache, but writes to disk are not copied again.] >P.S. >This is not scatter/gatther but it's so simple :-) I think there are other problems like all the copying done in stream.c. There is a flag on streamwrite(), docopy, that is just ignored now with a comment: /* * docopy will be used if I can ever figure out when to * avoid copying data -- presotto */ It would *not* be easy to fix that one (which is why the comment is there and not the code) since one of the features of streams is to buffer data. To use that flag the write call would have to be pushed through the stack before the return. Of course, that is another feature of streams; to push data through the stack without a context switch, but if you can't, perhaps then copy it? [On write, if there is a blocked read at the "end" and it wants enough data to hold the entire write, "doit". Imagine streams that add data, like IP, the "if it will fit" would be "interesting".] Eventually, the data has to be copied somewhere. The goal would be to only do it once. During a user space write that goes straight to the mnt driver, the data gets copied at least 3 times! User space to RPC (convS2M). RPC to protocol stream block (streamwrite()). RPC stream block to ethernet device (etherwrite()). (With the protocols, it could happen a few more times because of copyb/pullup/expandb.) A read is no better or worse. In "Plan 9: The Documents" Introduction, page 20, 5th paragraph I quote: "What would we do differently next time? Some elements of the implementation are unsatisfactory. Using streams to implement network interfaces in the kernel allows protocols to be connected together dynamically, such as to attach the same TTY driver to TCP, URP, and IL connections, but Plan 9 makes no use of the configurability. (It was exploited, however, in the research UNIX system for which streams were invented.) Replacing streams by static I/O queues would simplify the code and make it faster." We know Plan9 version 1 and 2 have streams. I think Plan9 version 3 (in progress) still has them. I found a use for them last week while on "vacation". I had a laptop with no ethernet interface needing to write/test network based applications. I wrote a quick little "localhost" interface that worked well. In /rc/bin/termrc, add the "if not": if (test -e /net/ether0/clone) ip/ipconfig if not { ip/loconfig `{ndb/query sys pc ip} # the binds are necessary since there were no interfaces # when /lib/namespace was evaluated bind -b '#Iudp' /net bind -b '#Itcp' /net bind -b '#Iil' /net } If you don't specify an address, it defaults to 127.0.0.1. Source to loconfig.c is at the end. (BTW, it works on the 4 disk set, even though you would have to write network apps yourself.) As far as scatter/gather goes, unless many transfers are < MAXFDATA, readv/writev wouldn't do much good. David Butler gdb@dbSystems.com ======================= loconfig.c ==================== /* * I put this code is in the public domain. * Long Live Plan9. David Butler gdb@dbSystems.com * * Create a "local" interface. Once this is done, * bind '#I...' to /net and go. * * I don't do a "push permanent" so a kill of loconfig * will remove the interface. You can create as many * as you want (well, there is a limit, look at the * kernel source.) */ #include #include #include static void error(char *s) { print("%s: %r\n", s); exits(s); } void main(int argc, char *argv[]) { static char buf[1600]; uchar addr[4]; int fd, n; if (--argc) { /* * this doesn't work very well. * parseip() doesn't return < 0 * for anything I can tell * I'll fix that some day... */ if (parseip(addr, argv[1]) < 0) { print("%s: invalid ip address %s\n", argv[0], argv[1]); exits("parseip"); } } else { parseip(addr, "127.0.0.1"); } if ((n = rfork( RFPROC|RFNOWAIT|RFCFDG|RFCNAMEG|RFCENVG|RFNOTEG)) < 0) { error("fork"); } if (n) { exits(0); } fmtinstall('I', eipconv); if (bind("#c", "/dev", MREPL) < 0) { error("bind"); /* can't see it yet */ } if (open("/dev/cons", OREAD)) { error("open stdin"); /* still can't see it ... */ } if (open("/dev/cons", OWRITE) != 1) { error("open stdout"); /* still can't see it ... */ } if (dup(1, 2) < 0) { error("open stderr"); /* Now, it can be seen */ } if (bind("#|", "/net", MREPL) < 0) { error("bind"); } if ((n = open("/net/ctl", ORDWR)) < 0) { error("openc"); } strcpy(buf, "push internet"); if (write(n, buf, strlen(buf)) != strlen(buf)) { error(buf); } sprint(buf, "setip %I 255.255.255.255", addr); if (write(n, buf, strlen(buf)) != strlen(buf)) { error(buf); } if ((fd = open("/net/data1", ORDWR)) < 0) { error("opend"); } while ((n = read(fd, buf, sizeof(buf))) >= 0) { write(fd, buf, n); } } END of loconfig.c