From ftlofaro@unlv.edu Fri Jun 3 15:17:10 EDT 1994 Article: 8742 of comp.os.linux.admin Newsgroups: comp.os.linux.admin,comp.os.linux.help,comp.os.linux.development Path: bigblue.oit.unc.edu!concert!gatech!howland.reston.ans.net!pipex!uknet!EU.net!uunet!news.nevada.edu!jimi!ftlofaro From: ftlofaro@unlv.edu (Frank Lofaro) Subject: NFS over TCP!!! (Re: NFS and PPP on Linux REALLY slow) Message-ID: <1994Jun3.072718.1485@unlv.edu> Sender: news@unlv.edu (News User) Organization: University of Nevada, Las Vegas References: <2sjfk7$aed@clarknet.clark.net> Date: Fri, 3 Jun 94 07:27:18 GMT Lines: 197 Xref: bigblue.oit.unc.edu comp.os.linux.admin:8742 comp.os.linux.help:36834 comp.os.linux.development:10111 In article at@setanta.demon.co.uk (Amrik Thethi) writes: >I wouldn't use NFS over a serial link, PPP or SLIP since Sun NFS >doesn't use UDP checksums. This is OK for any ethernet network, but >is likely to cause file corruption on a dodgy phone link. What you >need is a TCP based NFS, which isn't currently available AFAIK Well, I've got some ALPHA code I hacked up. The mount is screwed if the TCP connection closes. Then all access give EPIPE until it is umounted and remount. I haven't yet figured out how to best have the kernel reestablish the TCP connection. Other than that it seems to work. You need to hack up mount to use a SOCK_STREAM instead of a SOCK_DGRAM (get the mount source and add that feature, I just quick hacked that on my system by making a separate TCP NFS mount binary). NOTE: I posted this also to c.o.l.d since they might like to check out this code. Please don't follow up there unless its related to kernel development though. Thanks. Here's the kernel patch. It is against one of the 1.1.x patches (x is around 15), but it should go into newer kernels including 1.1.18 okay. It should also go into any older kernels as long as they are somewhat recent. _*NO WARRANTY!!! ALPHA CODE!!!*_ -----BEGIN KERNEL PATCH BLOCK---- diff -r -u -N linux.ref/fs/nfs/sock.c linux/fs/nfs/sock.c --- linux.ref/fs/nfs/sock.c Mon Feb 28 22:03:41 1994 +++ linux/fs/nfs/sock.c Sat Apr 30 17:34:09 1994 @@ -3,6 +3,8 @@ * * Copyright (C) 1992, 1993 Rick Sladkey * + * NFS over TCP by Frank Lofaro + * * low-level nfs remote procedure call interface */ @@ -15,6 +17,7 @@ #include #include #include +#include extern struct socket *socki_lookup(struct inode *inode); @@ -31,6 +34,123 @@ * to the server socket. */ + +static inline int do_sock_send(struct socket *sock, void *start, int size, + int noblock, unsigned flags) +{ + int retval; + int origsize; + origsize=size; + while (size) { + retval=sock->ops->send(sock, start, size, noblock, flags); + if (retval < 0) + return retval; + if (!retval) { + printk(KERN_WARNING "do_sock_send: send returns 0!\n"); + return -EIO; + } + size -= retval; + start = (void *) ((char *) start) + retval; + } + return origsize; +} + +static inline int do_sock_recv(struct socket *sock, void *start, int size, + int noblock, unsigned flags) +{ + int retval; + int origsize; + origsize=size; + while (size) { + retval=sock->ops->recv(sock, start, size, noblock, flags); + if (retval < 0) + return retval; + if (!retval) { + printk(KERN_WARNING "do_sock_recv: EOF!\n"); + return -EIO; + } + size -= retval; + start = (void *) ((char *) start) + retval; + } + return origsize; +} + +static inline int do_rpc_send(struct socket *sock, void *start, int size, int noblock, + unsigned flags) +{ + unsigned long *reclenptr; /* Must be 4 bytes. See RFC 1057 */ + unsigned long save; + int retval; + if (sock->type == SOCK_STREAM) { + if (size < 4) + reclenptr=(long *) kmalloc(sizeof(long), GFP_KERNEL); + else { + reclenptr=(unsigned long *) start; + save=*reclenptr; + } + *reclenptr=htonl(size | (1 << 31)); + printk(KERN_DEBUG "send size: %d\n", size); + printk(KERN_DEBUG "send reclen: %8lx\n", *reclenptr); + retval=do_sock_send(sock, reclenptr, 4, noblock, flags); + if (size < 4) + kfree(reclenptr); + else + *reclenptr=save; + printk(KERN_DEBUG "send retval: %d\n", retval); + if (retval < 0) + return retval; + } + return do_sock_send(sock, start, size, noblock, flags); +} + +static inline int do_rpc_recv(struct socket *sock, void *start, int size, int noblock, + unsigned flags) +{ + unsigned long *reclenptr; /* Must be 4 bytes. See RFC 1057 */ + unsigned long reclen; + unsigned int recsize; + int more=1; + int retval; + if (sock->type == SOCK_STREAM) { + if (size < 4) + reclenptr=(unsigned long *) + kmalloc(sizeof(long), GFP_KERNEL); + else + reclenptr=(unsigned long *) start; + retval=do_sock_recv(sock, reclenptr, 4, noblock, flags); + printk(KERN_DEBUG "recv retval 1: %u\n", retval); + reclen=*reclenptr; + printk(KERN_DEBUG "recv reclen: %8lx\n", reclen); + if (size < 4) + kfree(reclenptr); + if (!retval) { + printk(KERN_WARNING "EOF on NFS STREAM!\n"); + return -EIO; + } + if (retval < 0) + return retval; + recsize=ntohl(reclen); + more=(!(recsize & (1 << 31))); + if (more) + printk(KERN_NOTICE "recv: Fragment!\n"); + recsize &= ~(1 << 31); + printk(KERN_DEBUG "recv recsize: %u\n", recsize); + if (recsize > size) { + printk(KERN_WARNING "recv: recsize > size!\n"); + return -EIO; + } + retval=do_sock_recv(sock, start, recsize, noblock, flags); + if (!retval) { + printk(KERN_WARNING "recv: EOF on NFS STREAM!\n"); + return -EIO; + } + printk(KERN_DEBUG "recv retval 2: %u\n", retval); + return retval; + } else { + return sock->ops->recv(sock, start, size, noblock, flags); + } +} + static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end) { struct file *file; @@ -81,9 +201,9 @@ fs = get_fs(); set_fs(get_ds()); for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1) { - result = sock->ops->send(sock, (void *) start, len, 0, 0); + result = do_rpc_send(sock, (void *) start, len, 0, 0); if (result < 0) { - printk("nfs_rpc_call: send error = %d\n", result); + printk("nfs_rpc_call: send error = %d\n", -result); break; } re_select: @@ -129,8 +249,7 @@ remove_wait_queue(entry.wait_address, &entry.wait); current->state = TASK_RUNNING; addrlen = 0; - result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0, - NULL, &addrlen); + result = do_rpc_recv(sock, (void *) start, PAGE_SIZE, 1, 0); if (result < 0) { if (result == -EAGAIN) { #if 0 -----END KERNEL PATCH BLOCK---- end.