/* * linux/fs/nfs/sock.c * * Copyright (C) 1992 Rick Sladkey * * low-level nfs remote procedure call interface */ #include #include #include #include #include #include #include "../../net/kern_sock.h" extern struct socket *socki_lookup(struct inode *inode); /* * We violate some modularity principles here by poking around * in some socket internals. Besides having to call socket * functions from kernel-space instead of user space, the socket * interface does not lend itself well to being cleanly called * without a file descriptor. Since the nfs calls can run on * behalf of any process, the superblock maintains a file pointer * to the server socket. */ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end) { struct file *file; struct inode *inode; struct socket *sock; unsigned short fs; int result; int xid; int len; select_table wait_table; struct select_table_entry entry; int (*select) (struct inode *, struct file *, int, select_table *); int init_timeout, max_timeout; int timeout; int retrans; int major_timeout_seen; char *server_name; int n; xid = start[0]; len = ((char *) end) - ((char *) start); file = server->file; inode = file->f_inode; select = file->f_op->select; sock = socki_lookup(inode); init_timeout = server->timeo; max_timeout = NFS_MAX_RPC_TIMEOUT*HZ/10; retrans = server->retrans; major_timeout_seen = 0; server_name = server->hostname; if (!sock) { printk("nfs_rpc_call: socki_lookup failed\n"); return -EBADF; } __asm__("mov %%fs,%0":"=r" (fs)); __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1) { result = sock->ops->send(sock, (void *) start, len, 0, 0); if (result < 0) { printk("nfs_rpc_call: send error = %d\n", result); break; } re_select: wait_table.nr = 0; wait_table.entry = &entry; current->state = TASK_INTERRUPTIBLE; if (!select(inode, file, SEL_IN, &wait_table) && !select(inode, file, SEL_IN, NULL)) { if (timeout > max_timeout) timeout = max_timeout; current->timeout = jiffies + timeout; schedule(); remove_wait_queue(entry.wait_address, &entry.wait); current->state = TASK_RUNNING; if (current->signal & ~current->blocked) { #if 0 /* doesn't work yet */ if (!(server->flags & NFS_MOUNT_INTR)) goto re_select; #endif current->timeout = 0; result = -EINTR; break; } if (!current->timeout) { if (n < retrans) continue; if (server->flags & NFS_MOUNT_SOFT) { printk("NFS server %s not responding, " "timed out", server_name); result = -EIO; break; } n = 0; timeout = init_timeout; init_timeout <<= 1; if (!major_timeout_seen) { printk("NFS server %s not responding, " "still trying\n", server_name); } major_timeout_seen = 1; continue; } else current->timeout = 0; } else if (wait_table.nr) remove_wait_queue(entry.wait_address, &entry.wait); current->state = TASK_RUNNING; result = sock->ops->recv(sock, (void *) start, 4096, 1, 0); if (result < 0) { if (result == -EAGAIN) { printk("nfs_rpc_call: bad select ready\n"); goto re_select; } if (result != -ERESTARTSYS) { printk("nfs_rpc_call: recv error = %d\n", -result); } break; } if (*start == xid) { if (major_timeout_seen) printk("NFS server %s OK\n", server_name); break; } #if 0 printk("nfs_rpc_call: XID mismatch\n"); #endif } __asm__("mov %0,%%fs"::"r" (fs)); return result; } /* * For now we lock out other simulaneous nfs calls for the same filesytem * because we are single-threaded and don't want to get mismatched * RPC replies. */ int nfs_rpc_call(struct nfs_server *server, int *start, int *end) { int result; while (server->lock) sleep_on(&server->wait); server->lock = 1; result = do_nfs_rpc_call(server, start, end); server->lock = 0; wake_up(&server->wait); return result; }