Skip to content

Commit

Permalink
Merge tag 'nfs-for-3.7-4' of git://git.linux-nfs.org/projects/trondmy…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
torvalds committed Nov 3, 2012
2 parents 225ff86 + 998f40b commit d416497
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 35 deletions.
5 changes: 3 additions & 2 deletions fs/nfs/dns_resolve.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
{
char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
struct nfs_dns_ent key, *item;
unsigned long ttl;
unsigned int ttl;
ssize_t len;
int ret = -EINVAL;

Expand All @@ -240,7 +240,8 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
key.namelen = len;
memset(&key.h, 0, sizeof(key.h));

ttl = get_expiry(&buf);
if (get_uint(&buf, &ttl) < 0)
goto out;
if (ttl == 0)
goto out;
key.h.expiry_time = ttl + seconds_since_boot();
Expand Down
5 changes: 4 additions & 1 deletion fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,10 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
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);
}
Expand Down
6 changes: 4 additions & 2 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,12 @@ extern int __init register_nfs_fs(void);
extern void __exit unregister_nfs_fs(void);
extern void nfs_sb_active(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 */
#define NFS_PATH_CANONICAL 1
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);
struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
Expand Down Expand Up @@ -498,7 +500,7 @@ static inline char *nfs_devname(struct dentry *dentry,
char *buffer, ssize_t buflen)
{
char *dummy;
return nfs_path(&dummy, dentry, buffer, buflen);
return nfs_path(&dummy, dentry, buffer, buflen, NFS_PATH_CANONICAL);
}

/*
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/mount_clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ int nfs_mount(struct nfs_mount_request *info)
else
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);

if (status < 0)
Expand Down
19 changes: 14 additions & 5 deletions fs/nfs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,22 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
* @dentry - pointer to dentry
* @buffer - result buffer
* @buflen - length of buffer
* @flags - options (see below)
*
* Helper function for constructing the server pathname
* by arbitrary hashed dentry.
*
* This is mainly for use in figuring out the path on the
* server side when automounting on top of an existing partition
* 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;
int namelen;
Expand Down Expand Up @@ -74,7 +81,7 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
rcu_read_unlock();
goto rename_retry;
}
if (*end != '/') {
if ((flags & NFS_PATH_CANONICAL) && *end != '/') {
if (--buflen < 0) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
Expand All @@ -91,9 +98,11 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
return end;
}
namelen = strlen(base);
/* Strip off excess slashes in base string */
while (namelen > 0 && base[namelen - 1] == '/')
namelen--;
if (flags & NFS_PATH_CANONICAL) {
/* Strip off excess slashes in base string */
while (namelen > 0 && base[namelen - 1] == '/')
namelen--;
}
buflen -= namelen;
if (buflen < 0) {
spin_unlock(&dentry->d_lock);
Expand Down
3 changes: 2 additions & 1 deletion fs/nfs/nfs4namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
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)) {
char *path_component = nfs_path_component(path, limit);
if (path_component)
Expand Down
46 changes: 28 additions & 18 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
dprintk("%s ERROR: %d Reset session\n", __func__,
errorcode);
nfs4_schedule_session_recovery(clp->cl_session, errorcode);
exception->retry = 1;
break;
goto wait_on_recovery;
#endif /* defined(CONFIG_NFS_V4_1) */
case -NFS4ERR_FILE_OPEN:
if (exception->timeout > HZ) {
Expand Down Expand Up @@ -1572,9 +1571,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
data->timestamp = jiffies;
if (nfs4_setup_sequence(data->o_arg.server,
&data->o_arg.seq_args,
&data->o_res.seq_res, task))
return;
rpc_call_start(task);
&data->o_res.seq_res,
task) != 0)
nfs_release_seqid(data->o_arg.seqid);
else
rpc_call_start(task);
return;
unlock_no_action:
rcu_read_unlock();
Expand Down Expand Up @@ -1748,7 +1749,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred,

/* even though OPEN succeeded, access is denied. Close the file */
nfs4_close_state(state, fmode);
return -NFS4ERR_ACCESS;
return -EACCES;
}

/*
Expand Down Expand Up @@ -2196,7 +2197,7 @@ static void nfs4_free_closedata(void *data)
nfs4_put_open_state(calldata->state);
nfs_free_seqid(calldata->arg.seqid);
nfs4_put_state_owner(sp);
nfs_sb_deactive(sb);
nfs_sb_deactive_async(sb);
kfree(calldata);
}

Expand Down Expand Up @@ -2296,9 +2297,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
if (nfs4_setup_sequence(NFS_SERVER(inode),
&calldata->arg.seq_args,
&calldata->res.seq_res,
task))
goto out;
rpc_call_start(task);
task) != 0)
nfs_release_seqid(calldata->arg.seqid);
else
rpc_call_start(task);
out:
dprintk("%s: done!\n", __func__);
}
Expand Down Expand Up @@ -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)
rpc_restart_call_prepare(task);
}
nfs_release_seqid(calldata->arg.seqid);
}

static void nfs4_locku_prepare(struct rpc_task *task, void *data)
Expand All @@ -4545,9 +4548,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
calldata->timestamp = jiffies;
if (nfs4_setup_sequence(calldata->server,
&calldata->arg.seq_args,
&calldata->res.seq_res, task))
return;
rpc_call_start(task);
&calldata->res.seq_res,
task) != 0)
nfs_release_seqid(calldata->arg.seqid);
else
rpc_call_start(task);
}

static const struct rpc_call_ops nfs4_locku_ops = {
Expand Down Expand Up @@ -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? */
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
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.new_lock_owner = 1;
data->res.open_seqid = data->arg.open_seqid;
Expand All @@ -4701,10 +4706,15 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
data->timestamp = jiffies;
if (nfs4_setup_sequence(data->server,
&data->arg.seq_args,
&data->res.seq_res, task))
&data->res.seq_res,
task) == 0) {
rpc_call_start(task);
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)
Expand Down Expand Up @@ -5667,7 +5677,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl,
tbl->slots = new;
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++)
tbl->slots[i].seq_nr = ivalue;
spin_unlock(&tbl->slot_tbl_lock);
Expand Down
4 changes: 2 additions & 2 deletions fs/nfs/pnfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -925,8 +925,8 @@ pnfs_find_alloc_layout(struct inode *ino,
if (likely(nfsi->layout == NULL)) { /* Won the race? */
nfsi->layout = new;
return new;
}
pnfs_free_layout_hdr(new);
} else if (new != NULL)
pnfs_free_layout_hdr(new);
out_existing:
pnfs_get_layout_hdr(nfsi->layout);
return nfsi->layout;
Expand Down
51 changes: 50 additions & 1 deletion fs/nfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include <linux/parser.h>
#include <linux/nsproxy.h>
#include <linux/rcupdate.h>
#include <linux/kthread.h>

#include <asm/uaccess.h>

Expand Down Expand Up @@ -415,6 +416,54 @@ void nfs_sb_deactive(struct super_block *sb)
}
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
*/
Expand Down Expand Up @@ -771,7 +820,7 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root)
int err = 0;
if (!page)
return -ENOMEM;
devname = nfs_path(&dummy, root, page, PAGE_SIZE);
devname = nfs_path(&dummy, root, page, PAGE_SIZE, 0);
if (IS_ERR(devname))
err = PTR_ERR(devname);
else
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/unlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata)

nfs_dec_sillycount(data->dir);
nfs_free_unlinkdata(data);
nfs_sb_deactive(sb);
nfs_sb_deactive_async(sb);
}

static void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
Expand Down
2 changes: 1 addition & 1 deletion net/sunrpc/backchannel_rqst.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs)
xprt_free_allocation(req);

dprintk("RPC: setup backchannel transport failed\n");
return -1;
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(xprt_setup_backchannel);

Expand Down

0 comments on commit d416497

Please sign in to comment.