Commit d4164973 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.7-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:

 - Fix a bunch of deadlock situations:
   * State recovery can deadlock if we fail to release sequence ids
     before scheduling the recovery thread.
   * Calling deactivate_super() from an RPC workqueue thread can
     deadlock because of the call to rpc_shutdown_client.

 - Display the device name correctly in /proc/*/mounts

 - Fix a number of incorrect error return values:
   * When NFSv3 mounts fail due to a timeout.
   * On NFSv4.1 backchannel setup failure
   * On NFSv4 open access checks

 - pnfs_find_alloc_layout() must check the layout pointer for NULL

 - Fix a regression in the legacy DNS resolved

* tag 'nfs-for-3.7-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS4: nfs4_opendata_access should return errno
  NFSv4: Initialise the NFSv4.1 slot table highest_used_slotid correctly
  SUNRPC: return proper errno from backchannel_rqst
  NFS: add nfs_sb_deactive_async to avoid deadlock
  nfs: Show original device name verbatim in /proc/*/mount{s,info}
  nfsv3: Make v3 mounts fail with ETIMEDOUTs instead EIO on mountd timeouts
  nfs: Check whether a layout pointer is NULL before free it
  NFS: fix bug in legacy DNS resolver.
  NFSv4: nfs4_locku_done must release the sequence id
  NFSv4.1: We must release the sequence id when we fail to get a session slot
  NFS: Wait for session recovery to finish before returning
parents 225ff868 998f40b5
...@@ -217,7 +217,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen) ...@@ -217,7 +217,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
{ {
char buf1[NFS_DNS_HOSTNAME_MAXLEN+1]; char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
struct nfs_dns_ent key, *item; struct nfs_dns_ent key, *item;
unsigned long ttl; unsigned int ttl;
ssize_t len; ssize_t len;
int ret = -EINVAL; int ret = -EINVAL;
...@@ -240,7 +240,8 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen) ...@@ -240,7 +240,8 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
key.namelen = len; key.namelen = len;
memset(&key.h, 0, sizeof(key.h)); memset(&key.h, 0, sizeof(key.h));
ttl = get_expiry(&buf); if (get_uint(&buf, &ttl) < 0)
goto out;
if (ttl == 0) if (ttl == 0)
goto out; goto out;
key.h.expiry_time = ttl + seconds_since_boot(); key.h.expiry_time = ttl + seconds_since_boot();
......
...@@ -685,7 +685,10 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) ...@@ -685,7 +685,10 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
if (ctx->cred != NULL) if (ctx->cred != NULL)
put_rpccred(ctx->cred); put_rpccred(ctx->cred);
dput(ctx->dentry); dput(ctx->dentry);
nfs_sb_deactive(sb); if (is_sync)
nfs_sb_deactive(sb);
else
nfs_sb_deactive_async(sb);
kfree(ctx->mdsthreshold); kfree(ctx->mdsthreshold);
kfree(ctx); kfree(ctx);
} }
......
...@@ -351,10 +351,12 @@ extern int __init register_nfs_fs(void); ...@@ -351,10 +351,12 @@ extern int __init register_nfs_fs(void);
extern void __exit unregister_nfs_fs(void); extern void __exit unregister_nfs_fs(void);
extern void nfs_sb_active(struct super_block *sb); extern void nfs_sb_active(struct super_block *sb);
extern void nfs_sb_deactive(struct super_block *sb); extern void nfs_sb_deactive(struct super_block *sb);
extern void nfs_sb_deactive_async(struct super_block *sb);
/* namespace.c */ /* namespace.c */
#define NFS_PATH_CANONICAL 1
extern char *nfs_path(char **p, struct dentry *dentry, extern char *nfs_path(char **p, struct dentry *dentry,
char *buffer, ssize_t buflen); char *buffer, ssize_t buflen, unsigned flags);
extern struct vfsmount *nfs_d_automount(struct path *path); extern struct vfsmount *nfs_d_automount(struct path *path);
struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *, struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *); struct nfs_fh *, struct nfs_fattr *);
...@@ -498,7 +500,7 @@ static inline char *nfs_devname(struct dentry *dentry, ...@@ -498,7 +500,7 @@ static inline char *nfs_devname(struct dentry *dentry,
char *buffer, ssize_t buflen) char *buffer, ssize_t buflen)
{ {
char *dummy; char *dummy;
return nfs_path(&dummy, dentry, buffer, buflen); return nfs_path(&dummy, dentry, buffer, buflen, NFS_PATH_CANONICAL);
} }
/* /*
......
...@@ -181,7 +181,7 @@ int nfs_mount(struct nfs_mount_request *info) ...@@ -181,7 +181,7 @@ int nfs_mount(struct nfs_mount_request *info)
else else
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT]; msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT];
status = rpc_call_sync(mnt_clnt, &msg, 0); status = rpc_call_sync(mnt_clnt, &msg, RPC_TASK_SOFT|RPC_TASK_TIMEOUT);
rpc_shutdown_client(mnt_clnt); rpc_shutdown_client(mnt_clnt);
if (status < 0) if (status < 0)
......
...@@ -33,6 +33,7 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ; ...@@ -33,6 +33,7 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
* @dentry - pointer to dentry * @dentry - pointer to dentry
* @buffer - result buffer * @buffer - result buffer
* @buflen - length of buffer * @buflen - length of buffer
* @flags - options (see below)
* *
* Helper function for constructing the server pathname * Helper function for constructing the server pathname
* by arbitrary hashed dentry. * by arbitrary hashed dentry.
...@@ -40,8 +41,14 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ; ...@@ -40,8 +41,14 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
* This is mainly for use in figuring out the path on the * This is mainly for use in figuring out the path on the
* server side when automounting on top of an existing partition * server side when automounting on top of an existing partition
* and in generating /proc/mounts and friends. * and in generating /proc/mounts and friends.
*
* Supported flags:
* NFS_PATH_CANONICAL: ensure there is exactly one slash after
* the original device (export) name
* (if unset, the original name is returned verbatim)
*/ */
char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen) char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen,
unsigned flags)
{ {
char *end; char *end;
int namelen; int namelen;
...@@ -74,7 +81,7 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen) ...@@ -74,7 +81,7 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
rcu_read_unlock(); rcu_read_unlock();
goto rename_retry; goto rename_retry;
} }
if (*end != '/') { if ((flags & NFS_PATH_CANONICAL) && *end != '/') {
if (--buflen < 0) { if (--buflen < 0) {
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
rcu_read_unlock(); rcu_read_unlock();
...@@ -91,9 +98,11 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen) ...@@ -91,9 +98,11 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
return end; return end;
} }
namelen = strlen(base); namelen = strlen(base);
/* Strip off excess slashes in base string */ if (flags & NFS_PATH_CANONICAL) {
while (namelen > 0 && base[namelen - 1] == '/') /* Strip off excess slashes in base string */
namelen--; while (namelen > 0 && base[namelen - 1] == '/')
namelen--;
}
buflen -= namelen; buflen -= namelen;
if (buflen < 0) { if (buflen < 0) {
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
......
...@@ -81,7 +81,8 @@ static char *nfs_path_component(const char *nfspath, const char *end) ...@@ -81,7 +81,8 @@ static char *nfs_path_component(const char *nfspath, const char *end)
static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
{ {
char *limit; char *limit;
char *path = nfs_path(&limit, dentry, buffer, buflen); char *path = nfs_path(&limit, dentry, buffer, buflen,
NFS_PATH_CANONICAL);
if (!IS_ERR(path)) { if (!IS_ERR(path)) {
char *path_component = nfs_path_component(path, limit); char *path_component = nfs_path_component(path, limit);
if (path_component) if (path_component)
......
...@@ -339,8 +339,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc ...@@ -339,8 +339,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
dprintk("%s ERROR: %d Reset session\n", __func__, dprintk("%s ERROR: %d Reset session\n", __func__,
errorcode); errorcode);
nfs4_schedule_session_recovery(clp->cl_session, errorcode); nfs4_schedule_session_recovery(clp->cl_session, errorcode);
exception->retry = 1; goto wait_on_recovery;
break;
#endif /* defined(CONFIG_NFS_V4_1) */ #endif /* defined(CONFIG_NFS_V4_1) */
case -NFS4ERR_FILE_OPEN: case -NFS4ERR_FILE_OPEN:
if (exception->timeout > HZ) { if (exception->timeout > HZ) {
...@@ -1572,9 +1571,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) ...@@ -1572,9 +1571,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
data->timestamp = jiffies; data->timestamp = jiffies;
if (nfs4_setup_sequence(data->o_arg.server, if (nfs4_setup_sequence(data->o_arg.server,
&data->o_arg.seq_args, &data->o_arg.seq_args,
&data->o_res.seq_res, task)) &data->o_res.seq_res,
return; task) != 0)
rpc_call_start(task); nfs_release_seqid(data->o_arg.seqid);
else
rpc_call_start(task);
return; return;
unlock_no_action: unlock_no_action:
rcu_read_unlock(); rcu_read_unlock();
...@@ -1748,7 +1749,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred, ...@@ -1748,7 +1749,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred,
/* even though OPEN succeeded, access is denied. Close the file */ /* even though OPEN succeeded, access is denied. Close the file */
nfs4_close_state(state, fmode); nfs4_close_state(state, fmode);
return -NFS4ERR_ACCESS; return -EACCES;
} }
/* /*
...@@ -2196,7 +2197,7 @@ static void nfs4_free_closedata(void *data) ...@@ -2196,7 +2197,7 @@ static void nfs4_free_closedata(void *data)
nfs4_put_open_state(calldata->state); nfs4_put_open_state(calldata->state);
nfs_free_seqid(calldata->arg.seqid); nfs_free_seqid(calldata->arg.seqid);
nfs4_put_state_owner(sp); nfs4_put_state_owner(sp);
nfs_sb_deactive(sb); nfs_sb_deactive_async(sb);
kfree(calldata); kfree(calldata);
} }
...@@ -2296,9 +2297,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) ...@@ -2296,9 +2297,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
if (nfs4_setup_sequence(NFS_SERVER(inode), if (nfs4_setup_sequence(NFS_SERVER(inode),
&calldata->arg.seq_args, &calldata->arg.seq_args,
&calldata->res.seq_res, &calldata->res.seq_res,
task)) task) != 0)
goto out; nfs_release_seqid(calldata->arg.seqid);
rpc_call_start(task); else
rpc_call_start(task);
out: out:
dprintk("%s: done!\n", __func__); dprintk("%s: done!\n", __func__);
} }
...@@ -4529,6 +4531,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) ...@@ -4529,6 +4531,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN) if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
} }
nfs_release_seqid(calldata->arg.seqid);
} }
static void nfs4_locku_prepare(struct rpc_task *task, void *data) static void nfs4_locku_prepare(struct rpc_task *task, void *data)
...@@ -4545,9 +4548,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) ...@@ -4545,9 +4548,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
calldata->timestamp = jiffies; calldata->timestamp = jiffies;
if (nfs4_setup_sequence(calldata->server, if (nfs4_setup_sequence(calldata->server,
&calldata->arg.seq_args, &calldata->arg.seq_args,
&calldata->res.seq_res, task)) &calldata->res.seq_res,
return; task) != 0)
rpc_call_start(task); nfs_release_seqid(calldata->arg.seqid);
else
rpc_call_start(task);
} }
static const struct rpc_call_ops nfs4_locku_ops = { static const struct rpc_call_ops nfs4_locku_ops = {
...@@ -4692,7 +4697,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) ...@@ -4692,7 +4697,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
/* Do we need to do an open_to_lock_owner? */ /* Do we need to do an open_to_lock_owner? */
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) { if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0)
return; goto out_release_lock_seqid;
data->arg.open_stateid = &state->stateid; data->arg.open_stateid = &state->stateid;
data->arg.new_lock_owner = 1; data->arg.new_lock_owner = 1;
data->res.open_seqid = data->arg.open_seqid; data->res.open_seqid = data->arg.open_seqid;
...@@ -4701,10 +4706,15 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) ...@@ -4701,10 +4706,15 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
data->timestamp = jiffies; data->timestamp = jiffies;
if (nfs4_setup_sequence(data->server, if (nfs4_setup_sequence(data->server,
&data->arg.seq_args, &data->arg.seq_args,
&data->res.seq_res, task)) &data->res.seq_res,
task) == 0) {
rpc_call_start(task);
return; return;
rpc_call_start(task); }
dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status); nfs_release_seqid(data->arg.open_seqid);
out_release_lock_seqid:
nfs_release_seqid(data->arg.lock_seqid);
dprintk("%s: done!, ret = %d\n", __func__, task->tk_status);
} }
static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata) static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
...@@ -5667,7 +5677,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, ...@@ -5667,7 +5677,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl,
tbl->slots = new; tbl->slots = new;
tbl->max_slots = max_slots; tbl->max_slots = max_slots;
} }
tbl->highest_used_slotid = -1; /* no slot is currently used */ tbl->highest_used_slotid = NFS4_NO_SLOT;
for (i = 0; i < tbl->max_slots; i++) for (i = 0; i < tbl->max_slots; i++)
tbl->slots[i].seq_nr = ivalue; tbl->slots[i].seq_nr = ivalue;
spin_unlock(&tbl->slot_tbl_lock); spin_unlock(&tbl->slot_tbl_lock);
......
...@@ -925,8 +925,8 @@ pnfs_find_alloc_layout(struct inode *ino, ...@@ -925,8 +925,8 @@ pnfs_find_alloc_layout(struct inode *ino,
if (likely(nfsi->layout == NULL)) { /* Won the race? */ if (likely(nfsi->layout == NULL)) { /* Won the race? */
nfsi->layout = new; nfsi->layout = new;
return new; return new;
} } else if (new != NULL)
pnfs_free_layout_hdr(new); pnfs_free_layout_hdr(new);
out_existing: out_existing:
pnfs_get_layout_hdr(nfsi->layout); pnfs_get_layout_hdr(nfsi->layout);
return nfsi->layout; return nfsi->layout;
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/nsproxy.h> #include <linux/nsproxy.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/kthread.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -415,6 +416,54 @@ void nfs_sb_deactive(struct super_block *sb) ...@@ -415,6 +416,54 @@ void nfs_sb_deactive(struct super_block *sb)
} }
EXPORT_SYMBOL_GPL(nfs_sb_deactive); EXPORT_SYMBOL_GPL(nfs_sb_deactive);
static int nfs_deactivate_super_async_work(void *ptr)
{
struct super_block *sb = ptr;
deactivate_super(sb);
module_put_and_exit(0);
return 0;
}
/*
* same effect as deactivate_super, but will do final unmount in kthread
* context
*/
static void nfs_deactivate_super_async(struct super_block *sb)
{
struct task_struct *task;
char buf[INET6_ADDRSTRLEN + 1];
struct nfs_server *server = NFS_SB(sb);
struct nfs_client *clp = server->nfs_client;
if (!atomic_add_unless(&sb->s_active, -1, 1)) {
rcu_read_lock();
snprintf(buf, sizeof(buf),
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
rcu_read_unlock();
__module_get(THIS_MODULE);
task = kthread_run(nfs_deactivate_super_async_work, sb,
"%s-deactivate-super", buf);
if (IS_ERR(task)) {
pr_err("%s: kthread_run: %ld\n",
__func__, PTR_ERR(task));
/* make synchronous call and hope for the best */
deactivate_super(sb);
module_put(THIS_MODULE);
}
}
}
void nfs_sb_deactive_async(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
if (atomic_dec_and_test(&server->active))
nfs_deactivate_super_async(sb);
}
EXPORT_SYMBOL_GPL(nfs_sb_deactive_async);
/* /*
* Deliver file system statistics to userspace * Deliver file system statistics to userspace
*/ */
...@@ -771,7 +820,7 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root) ...@@ -771,7 +820,7 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root)
int err = 0; int err = 0;
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
devname = nfs_path(&dummy, root, page, PAGE_SIZE); devname = nfs_path(&dummy, root, page, PAGE_SIZE, 0);
if (IS_ERR(devname)) if (IS_ERR(devname))
err = PTR_ERR(devname); err = PTR_ERR(devname);
else else
......
...@@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata) ...@@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata)
nfs_dec_sillycount(data->dir); nfs_dec_sillycount(data->dir);
nfs_free_unlinkdata(data); nfs_free_unlinkdata(data);
nfs_sb_deactive(sb); nfs_sb_deactive_async(sb);
} }
static void nfs_unlink_prepare(struct rpc_task *task, void *calldata) static void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
......
...@@ -172,7 +172,7 @@ int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs) ...@@ -172,7 +172,7 @@ int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs)
xprt_free_allocation(req); xprt_free_allocation(req);
dprintk("RPC: setup backchannel transport failed\n"); dprintk("RPC: setup backchannel transport failed\n");
return -1; return -ENOMEM;
} }
EXPORT_SYMBOL_GPL(xprt_setup_backchannel); EXPORT_SYMBOL_GPL(xprt_setup_backchannel);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment