NFS bugfixes for Linux 3.7
- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQlXRyAAoJEGcL54qWCgDy1G4QALUjERF6VBaDUVRVDe3cyrNr 1y5niKU19ysaXuNKtsGquVr3y72c0xyzCFLan8ZqJl1kXpYTDIyoDc0NBT3reffo T4rbw8SXVFVk+iVcJQgnFo5tblHVsVk+fvZPezBknaBb/uI/kxIbicZHHxvASyzM G0Ge/MC709x07tu3wJSyOtvD55j6bfpwbon6yjOKdN8/S2nOEsNOBjAunOWrKtsa 2v3uWC9APQqIF5z6dfhAVCEMRrQMame6W6b5LuY/MSQpLAwUUYeCGzjc1TMeWvnS n66iCqQ0TCVgJH+7fYnrzPm5uNk4Am2XhM3QY1B0j8zl73ZWIs4PfjdoBytLnqF+ SykNVUl72WqdJnWPCtk6LvoctyOsSMK5pJj4jadvNqDKgFkuRZkWb28bqux2OlHg ZCmnEZiew5tV+2anF6sgEaEhSyKNR/1h1ilXOy2TaV93qX+ec8tg+TE6C6YQR/R+ IoOoYD8Hbe/R/D76WK0xOOQbrXIQvKb1zvlIRx7W6d5gWpg9IceNLcBfCiF1GjPf 0PKxTbBSbSg44IRolPIprsZx6YzCI6wj2UzdPC7riozfZdSZ+T6Hh7z8rhhTjsHS D5emunX93kMp5QSSrw6EtdTkRJ++DB+cpjK5SP7UydHsWSGnKbd8eIv7viZu5J5O FLcHHWKIan9wQCslGQkO =NjEu -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
d4164973a0
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -33,6 +33,7 @@ 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.
|
||||
|
@ -40,8 +41,14 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
|
|||
* 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;
|
||||
|
@ -74,7 +81,7 @@ rename_retry:
|
|||
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();
|
||||
|
@ -91,9 +98,11 @@ rename_retry:
|
|||
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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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__);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include <linux/parser.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -172,7 +172,7 @@ out_free:
|
|||
xprt_free_allocation(req);
|
||||
|
||||
dprintk("RPC: setup backchannel transport failed\n");
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xprt_setup_backchannel);
|
||||
|
||||
|
|
Loading…
Reference in New Issue