Commit 5d538483 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.9-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 - Fix an NFSv4 idmapper regression
 - Fix an Oops in the pNFS blocks client
 - Fix up various issues with pNFS layoutcommit
 - Ensure correct read ordering of variables in
   rpc_wake_up_task_queue_locked

* tag 'nfs-for-3.9-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  SUNRPC: Add barriers to ensure read ordering in rpc_wake_up_task_queue_locked
  NFSv4.1: Add a helper pnfs_commit_and_return_layout
  NFSv4.1: Always clear the NFS_INO_LAYOUTCOMMIT in layoutreturn
  NFSv4.1: Fix a race in pNFS layoutcommit
  pnfs-block: removing DM device maybe cause oops when call dev_remove
  NFSv4: Fix the string length returned by the idmapper
parents a12183c6 1166fde6
...@@ -55,7 +55,8 @@ static void dev_remove(struct net *net, dev_t dev) ...@@ -55,7 +55,8 @@ static void dev_remove(struct net *net, dev_t dev)
bl_pipe_msg.bl_wq = &nn->bl_wq; bl_pipe_msg.bl_wq = &nn->bl_wq;
memset(msg, 0, sizeof(*msg)); memset(msg, 0, sizeof(*msg));
msg->data = kzalloc(1 + sizeof(bl_umount_request), GFP_NOFS); msg->len = sizeof(bl_msg) + bl_msg.totallen;
msg->data = kzalloc(msg->len, GFP_NOFS);
if (!msg->data) if (!msg->data)
goto out; goto out;
...@@ -66,7 +67,6 @@ static void dev_remove(struct net *net, dev_t dev) ...@@ -66,7 +67,6 @@ static void dev_remove(struct net *net, dev_t dev)
memcpy(msg->data, &bl_msg, sizeof(bl_msg)); memcpy(msg->data, &bl_msg, sizeof(bl_msg));
dataptr = (uint8_t *) msg->data; dataptr = (uint8_t *) msg->data;
memcpy(&dataptr[sizeof(bl_msg)], &bl_umount_request, sizeof(bl_umount_request)); memcpy(&dataptr[sizeof(bl_msg)], &bl_umount_request, sizeof(bl_umount_request));
msg->len = sizeof(bl_msg) + bl_msg.totallen;
add_wait_queue(&nn->bl_wq, &wq); add_wait_queue(&nn->bl_wq, &wq);
if (rpc_queue_upcall(nn->bl_device_pipe, msg) < 0) { if (rpc_queue_upcall(nn->bl_device_pipe, msg) < 0) {
......
...@@ -726,9 +726,9 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons, ...@@ -726,9 +726,9 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
return ret; return ret;
} }
static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data) static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data, size_t datalen)
{ {
return key_instantiate_and_link(key, data, strlen(data) + 1, return key_instantiate_and_link(key, data, datalen,
id_resolver_cache->thread_keyring, id_resolver_cache->thread_keyring,
authkey); authkey);
} }
...@@ -738,6 +738,7 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im, ...@@ -738,6 +738,7 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
struct key *key, struct key *authkey) struct key *key, struct key *authkey)
{ {
char id_str[NFS_UINT_MAXLEN]; char id_str[NFS_UINT_MAXLEN];
size_t len;
int ret = -ENOKEY; int ret = -ENOKEY;
/* ret = -ENOKEY */ /* ret = -ENOKEY */
...@@ -747,13 +748,15 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im, ...@@ -747,13 +748,15 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
case IDMAP_CONV_NAMETOID: case IDMAP_CONV_NAMETOID:
if (strcmp(upcall->im_name, im->im_name) != 0) if (strcmp(upcall->im_name, im->im_name) != 0)
break; break;
sprintf(id_str, "%d", im->im_id); /* Note: here we store the NUL terminator too */
ret = nfs_idmap_instantiate(key, authkey, id_str); len = sprintf(id_str, "%d", im->im_id) + 1;
ret = nfs_idmap_instantiate(key, authkey, id_str, len);
break; break;
case IDMAP_CONV_IDTONAME: case IDMAP_CONV_IDTONAME:
if (upcall->im_id != im->im_id) if (upcall->im_id != im->im_id)
break; break;
ret = nfs_idmap_instantiate(key, authkey, im->im_name); len = strlen(im->im_name);
ret = nfs_idmap_instantiate(key, authkey, im->im_name, len);
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
......
...@@ -129,7 +129,6 @@ static void filelayout_fenceme(struct inode *inode, struct pnfs_layout_hdr *lo) ...@@ -129,7 +129,6 @@ static void filelayout_fenceme(struct inode *inode, struct pnfs_layout_hdr *lo)
{ {
if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
return; return;
clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags);
pnfs_return_layout(inode); pnfs_return_layout(inode);
} }
......
...@@ -2632,7 +2632,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -2632,7 +2632,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
int status; int status;
if (pnfs_ld_layoutret_on_setattr(inode)) if (pnfs_ld_layoutret_on_setattr(inode))
pnfs_return_layout(inode); pnfs_commit_and_return_layout(inode);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
...@@ -6416,22 +6416,8 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) ...@@ -6416,22 +6416,8 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
static void nfs4_layoutcommit_release(void *calldata) static void nfs4_layoutcommit_release(void *calldata)
{ {
struct nfs4_layoutcommit_data *data = calldata; struct nfs4_layoutcommit_data *data = calldata;
struct pnfs_layout_segment *lseg, *tmp;
unsigned long *bitlock = &NFS_I(data->args.inode)->flags;
pnfs_cleanup_layoutcommit(data); pnfs_cleanup_layoutcommit(data);
/* Matched by references in pnfs_set_layoutcommit */
list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) {
list_del_init(&lseg->pls_lc_list);
if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT,
&lseg->pls_flags))
pnfs_put_lseg(lseg);
}
clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock);
smp_mb__after_clear_bit();
wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING);
put_rpccred(data->cred); put_rpccred(data->cred);
kfree(data); kfree(data);
} }
......
...@@ -417,6 +417,16 @@ should_free_lseg(struct pnfs_layout_range *lseg_range, ...@@ -417,6 +417,16 @@ should_free_lseg(struct pnfs_layout_range *lseg_range,
lo_seg_intersecting(lseg_range, recall_range); lo_seg_intersecting(lseg_range, recall_range);
} }
static bool pnfs_lseg_dec_and_remove_zero(struct pnfs_layout_segment *lseg,
struct list_head *tmp_list)
{
if (!atomic_dec_and_test(&lseg->pls_refcount))
return false;
pnfs_layout_remove_lseg(lseg->pls_layout, lseg);
list_add(&lseg->pls_list, tmp_list);
return true;
}
/* Returns 1 if lseg is removed from list, 0 otherwise */ /* Returns 1 if lseg is removed from list, 0 otherwise */
static int mark_lseg_invalid(struct pnfs_layout_segment *lseg, static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
struct list_head *tmp_list) struct list_head *tmp_list)
...@@ -430,11 +440,8 @@ static int mark_lseg_invalid(struct pnfs_layout_segment *lseg, ...@@ -430,11 +440,8 @@ static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
*/ */
dprintk("%s: lseg %p ref %d\n", __func__, lseg, dprintk("%s: lseg %p ref %d\n", __func__, lseg,
atomic_read(&lseg->pls_refcount)); atomic_read(&lseg->pls_refcount));
if (atomic_dec_and_test(&lseg->pls_refcount)) { if (pnfs_lseg_dec_and_remove_zero(lseg, tmp_list))
pnfs_layout_remove_lseg(lseg->pls_layout, lseg);
list_add(&lseg->pls_list, tmp_list);
rv = 1; rv = 1;
}
} }
return rv; return rv;
} }
...@@ -777,6 +784,21 @@ send_layoutget(struct pnfs_layout_hdr *lo, ...@@ -777,6 +784,21 @@ send_layoutget(struct pnfs_layout_hdr *lo,
return lseg; return lseg;
} }
static void pnfs_clear_layoutcommit(struct inode *inode,
struct list_head *head)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct pnfs_layout_segment *lseg, *tmp;
if (!test_and_clear_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags))
return;
list_for_each_entry_safe(lseg, tmp, &nfsi->layout->plh_segs, pls_list) {
if (!test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags))
continue;
pnfs_lseg_dec_and_remove_zero(lseg, head);
}
}
/* /*
* Initiates a LAYOUTRETURN(FILE), and removes the pnfs_layout_hdr * Initiates a LAYOUTRETURN(FILE), and removes the pnfs_layout_hdr
* when the layout segment list is empty. * when the layout segment list is empty.
...@@ -808,6 +830,7 @@ _pnfs_return_layout(struct inode *ino) ...@@ -808,6 +830,7 @@ _pnfs_return_layout(struct inode *ino)
/* Reference matched in nfs4_layoutreturn_release */ /* Reference matched in nfs4_layoutreturn_release */
pnfs_get_layout_hdr(lo); pnfs_get_layout_hdr(lo);
empty = list_empty(&lo->plh_segs); empty = list_empty(&lo->plh_segs);
pnfs_clear_layoutcommit(ino, &tmp_list);
pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL); pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL);
/* Don't send a LAYOUTRETURN if list was initially empty */ /* Don't send a LAYOUTRETURN if list was initially empty */
if (empty) { if (empty) {
...@@ -820,8 +843,6 @@ _pnfs_return_layout(struct inode *ino) ...@@ -820,8 +843,6 @@ _pnfs_return_layout(struct inode *ino)
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&tmp_list); pnfs_free_lseg_list(&tmp_list);
WARN_ON(test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags));
lrp = kzalloc(sizeof(*lrp), GFP_KERNEL); lrp = kzalloc(sizeof(*lrp), GFP_KERNEL);
if (unlikely(lrp == NULL)) { if (unlikely(lrp == NULL)) {
status = -ENOMEM; status = -ENOMEM;
...@@ -845,6 +866,33 @@ _pnfs_return_layout(struct inode *ino) ...@@ -845,6 +866,33 @@ _pnfs_return_layout(struct inode *ino)
} }
EXPORT_SYMBOL_GPL(_pnfs_return_layout); EXPORT_SYMBOL_GPL(_pnfs_return_layout);
int
pnfs_commit_and_return_layout(struct inode *inode)
{
struct pnfs_layout_hdr *lo;
int ret;
spin_lock(&inode->i_lock);
lo = NFS_I(inode)->layout;
if (lo == NULL) {
spin_unlock(&inode->i_lock);
return 0;
}
pnfs_get_layout_hdr(lo);
/* Block new layoutgets and read/write to ds */
lo->plh_block_lgets++;
spin_unlock(&inode->i_lock);
filemap_fdatawait(inode->i_mapping);
ret = pnfs_layoutcommit_inode(inode, true);
if (ret == 0)
ret = _pnfs_return_layout(inode);
spin_lock(&inode->i_lock);
lo->plh_block_lgets--;
spin_unlock(&inode->i_lock);
pnfs_put_layout_hdr(lo);
return ret;
}
bool pnfs_roc(struct inode *ino) bool pnfs_roc(struct inode *ino)
{ {
struct pnfs_layout_hdr *lo; struct pnfs_layout_hdr *lo;
...@@ -1458,7 +1506,6 @@ static void pnfs_ld_handle_write_error(struct nfs_write_data *data) ...@@ -1458,7 +1506,6 @@ static void pnfs_ld_handle_write_error(struct nfs_write_data *data)
dprintk("pnfs write error = %d\n", hdr->pnfs_error); dprintk("pnfs write error = %d\n", hdr->pnfs_error);
if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags & if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
PNFS_LAYOUTRET_ON_ERROR) { PNFS_LAYOUTRET_ON_ERROR) {
clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);
pnfs_return_layout(hdr->inode); pnfs_return_layout(hdr->inode);
} }
if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
...@@ -1613,7 +1660,6 @@ static void pnfs_ld_handle_read_error(struct nfs_read_data *data) ...@@ -1613,7 +1660,6 @@ static void pnfs_ld_handle_read_error(struct nfs_read_data *data)
dprintk("pnfs read error = %d\n", hdr->pnfs_error); dprintk("pnfs read error = %d\n", hdr->pnfs_error);
if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags & if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
PNFS_LAYOUTRET_ON_ERROR) { PNFS_LAYOUTRET_ON_ERROR) {
clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);
pnfs_return_layout(hdr->inode); pnfs_return_layout(hdr->inode);
} }
if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
...@@ -1746,11 +1792,27 @@ static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp) ...@@ -1746,11 +1792,27 @@ static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp)
list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) {
if (lseg->pls_range.iomode == IOMODE_RW && if (lseg->pls_range.iomode == IOMODE_RW &&
test_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags))
list_add(&lseg->pls_lc_list, listp); list_add(&lseg->pls_lc_list, listp);
} }
} }
static void pnfs_list_write_lseg_done(struct inode *inode, struct list_head *listp)
{
struct pnfs_layout_segment *lseg, *tmp;
unsigned long *bitlock = &NFS_I(inode)->flags;
/* Matched by references in pnfs_set_layoutcommit */
list_for_each_entry_safe(lseg, tmp, listp, pls_lc_list) {
list_del_init(&lseg->pls_lc_list);
pnfs_put_lseg(lseg);
}
clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock);
smp_mb__after_clear_bit();
wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING);
}
void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg) void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg)
{ {
pnfs_layout_io_set_failed(lseg->pls_layout, lseg->pls_range.iomode); pnfs_layout_io_set_failed(lseg->pls_layout, lseg->pls_range.iomode);
...@@ -1795,6 +1857,7 @@ void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data) ...@@ -1795,6 +1857,7 @@ void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data)
if (nfss->pnfs_curr_ld->cleanup_layoutcommit) if (nfss->pnfs_curr_ld->cleanup_layoutcommit)
nfss->pnfs_curr_ld->cleanup_layoutcommit(data); nfss->pnfs_curr_ld->cleanup_layoutcommit(data);
pnfs_list_write_lseg_done(data->args.inode, &data->lseg_list);
} }
/* /*
......
...@@ -219,6 +219,7 @@ void pnfs_set_layoutcommit(struct nfs_write_data *wdata); ...@@ -219,6 +219,7 @@ void pnfs_set_layoutcommit(struct nfs_write_data *wdata);
void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data);
int pnfs_layoutcommit_inode(struct inode *inode, bool sync); int pnfs_layoutcommit_inode(struct inode *inode, bool sync);
int _pnfs_return_layout(struct inode *); int _pnfs_return_layout(struct inode *);
int pnfs_commit_and_return_layout(struct inode *);
void pnfs_ld_write_done(struct nfs_write_data *); void pnfs_ld_write_done(struct nfs_write_data *);
void pnfs_ld_read_done(struct nfs_read_data *); void pnfs_ld_read_done(struct nfs_read_data *);
struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino, struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino,
...@@ -407,6 +408,11 @@ static inline int pnfs_return_layout(struct inode *ino) ...@@ -407,6 +408,11 @@ static inline int pnfs_return_layout(struct inode *ino)
return 0; return 0;
} }
static inline int pnfs_commit_and_return_layout(struct inode *inode)
{
return 0;
}
static inline bool static inline bool
pnfs_ld_layoutret_on_setattr(struct inode *inode) pnfs_ld_layoutret_on_setattr(struct inode *inode)
{ {
......
...@@ -180,6 +180,8 @@ static void __rpc_add_wait_queue(struct rpc_wait_queue *queue, ...@@ -180,6 +180,8 @@ static void __rpc_add_wait_queue(struct rpc_wait_queue *queue,
list_add_tail(&task->u.tk_wait.list, &queue->tasks[0]); list_add_tail(&task->u.tk_wait.list, &queue->tasks[0]);
task->tk_waitqueue = queue; task->tk_waitqueue = queue;
queue->qlen++; queue->qlen++;
/* barrier matches the read in rpc_wake_up_task_queue_locked() */
smp_wmb();
rpc_set_queued(task); rpc_set_queued(task);
dprintk("RPC: %5u added to queue %p \"%s\"\n", dprintk("RPC: %5u added to queue %p \"%s\"\n",
...@@ -430,8 +432,11 @@ static void __rpc_do_wake_up_task(struct rpc_wait_queue *queue, struct rpc_task ...@@ -430,8 +432,11 @@ static void __rpc_do_wake_up_task(struct rpc_wait_queue *queue, struct rpc_task
*/ */
static void rpc_wake_up_task_queue_locked(struct rpc_wait_queue *queue, struct rpc_task *task) static void rpc_wake_up_task_queue_locked(struct rpc_wait_queue *queue, struct rpc_task *task)
{ {
if (RPC_IS_QUEUED(task) && task->tk_waitqueue == queue) if (RPC_IS_QUEUED(task)) {
__rpc_do_wake_up_task(queue, task); smp_rmb();
if (task->tk_waitqueue == queue)
__rpc_do_wake_up_task(queue, task);
}
} }
/* /*
......
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