blob: ec6406470ac4897cc3e100ba05a1fc62f68edaf7 [file] [log] [blame]
/*
* 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;
}