Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d538483 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -55,7 +55,8 @@ static void dev_remove(struct net *net, dev_t dev)

	bl_pipe_msg.bl_wq = &nn->bl_wq;
	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)
		goto out;

@@ -66,7 +67,6 @@ static void dev_remove(struct net *net, dev_t dev)
	memcpy(msg->data, &bl_msg, sizeof(bl_msg));
	dataptr = (uint8_t *) msg->data;
	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);
	if (rpc_queue_upcall(nn->bl_device_pipe, msg) < 0) {
+8 −5
Original line number Diff line number Diff line
@@ -726,9 +726,9 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
	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,
					authkey);
}
@@ -738,6 +738,7 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
		struct key *key, struct key *authkey)
{
	char id_str[NFS_UINT_MAXLEN];
	size_t len;
	int ret = -ENOKEY;

	/* ret = -ENOKEY */
@@ -747,13 +748,15 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
	case IDMAP_CONV_NAMETOID:
		if (strcmp(upcall->im_name, im->im_name) != 0)
			break;
		sprintf(id_str, "%d", im->im_id);
		ret = nfs_idmap_instantiate(key, authkey, id_str);
		/* Note: here we store the NUL terminator too */
		len = sprintf(id_str, "%d", im->im_id) + 1;
		ret = nfs_idmap_instantiate(key, authkey, id_str, len);
		break;
	case IDMAP_CONV_IDTONAME:
		if (upcall->im_id != im->im_id)
			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;
	default:
		ret = -EINVAL;
+0 −1
Original line number Diff line number Diff line
@@ -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))
		return;
	clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags);
	pnfs_return_layout(inode);
}

+1 −15
Original line number Diff line number Diff line
@@ -2632,7 +2632,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
	int status;

	if (pnfs_ld_layoutret_on_setattr(inode))
		pnfs_return_layout(inode);
		pnfs_commit_and_return_layout(inode);

	nfs_fattr_init(fattr);
	
@@ -6416,22 +6416,8 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
static void nfs4_layoutcommit_release(void *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);
	/* 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);
	kfree(data);
}
+72 −9
Original line number Diff line number Diff line
@@ -417,6 +417,16 @@ should_free_lseg(struct pnfs_layout_range *lseg_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 */
static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
			     struct list_head *tmp_list)
@@ -430,12 +440,9 @@ static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
		 */
		dprintk("%s: lseg %p ref %d\n", __func__, lseg,
			atomic_read(&lseg->pls_refcount));
		if (atomic_dec_and_test(&lseg->pls_refcount)) {
			pnfs_layout_remove_lseg(lseg->pls_layout, lseg);
			list_add(&lseg->pls_list, tmp_list);
		if (pnfs_lseg_dec_and_remove_zero(lseg, tmp_list))
			rv = 1;
	}
	}
	return rv;
}

@@ -777,6 +784,21 @@ send_layoutget(struct pnfs_layout_hdr *lo,
	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
 * when the layout segment list is empty.
@@ -808,6 +830,7 @@ _pnfs_return_layout(struct inode *ino)
	/* Reference matched in nfs4_layoutreturn_release */
	pnfs_get_layout_hdr(lo);
	empty = list_empty(&lo->plh_segs);
	pnfs_clear_layoutcommit(ino, &tmp_list);
	pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL);
	/* Don't send a LAYOUTRETURN if list was initially empty */
	if (empty) {
@@ -820,8 +843,6 @@ _pnfs_return_layout(struct inode *ino)
	spin_unlock(&ino->i_lock);
	pnfs_free_lseg_list(&tmp_list);

	WARN_ON(test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags));

	lrp = kzalloc(sizeof(*lrp), GFP_KERNEL);
	if (unlikely(lrp == NULL)) {
		status = -ENOMEM;
@@ -845,6 +866,33 @@ _pnfs_return_layout(struct inode *ino)
}
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)
{
	struct pnfs_layout_hdr *lo;
@@ -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);
	if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
	    PNFS_LAYOUTRET_ON_ERROR) {
		clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);
		pnfs_return_layout(hdr->inode);
	}
	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)
	dprintk("pnfs read error = %d\n", hdr->pnfs_error);
	if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
	    PNFS_LAYOUTRET_ON_ERROR) {
		clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);
		pnfs_return_layout(hdr->inode);
	}
	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)

	list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) {
		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);
	}
}

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)
{
	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)

	if (nfss->pnfs_curr_ld->cleanup_layoutcommit)
		nfss->pnfs_curr_ld->cleanup_layoutcommit(data);
	pnfs_list_write_lseg_done(data->args.inode, &data->lseg_list);
}

/*
Loading