| /* |
| * linux/fs/nfs/sock.c |
| * |
| * Copyright (C) 1992 Rick Sladkey |
| * |
| * low-level nfs remote procedure call interface |
| */ |
| |
| #include <linux/config.h> |
| #include <linux/sched.h> |
| #include <linux/nfs_fs.h> |
| #include <linux/errno.h> |
| #include <linux/socket.h> |
| #include <linux/fcntl.h> |
| #include <asm/segment.h> |
| #include <linux/in.h> |
| #include <linux/net.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; |
| int addrlen; |
| |
| 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; |
| } |
| 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); |
| 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 = -ERESTARTSYS; |
| 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; |
| addrlen = 0; |
| result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0, |
| NULL, &addrlen); |
| if (result < 0) { |
| if (result == -EAGAIN) { |
| goto re_select; |
| #if 0 |
| printk("nfs_rpc_call: bad select ready\n"); |
| #endif |
| } |
| 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 |
| } |
| set_fs(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; |
| } |
| |