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

Commit 53f2c4a8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client updates from Trond Myklebust:
 "New features include:
   - Rewrite the O_DIRECT code so that it can share the same coalescing
     and pNFS functionality as the page cache code.
   - Allow the server to provide hints as to when we should use pNFS,
     and when it is more efficient to read and write through the
     metadata server.
   - NFS cache consistency updates:
     * Use the ctime to emulate a change attribute for NFSv2/v3 so that
       all NFS versions can share the same cache management code.
     * New cache management code will only look at the change attribute
       and size attribute when deciding whether or not our cached data
       is still valid or not.
     * Don't request NFSv4 post-op attributes on writes in cases such as
       O_DIRECT, where we don't care about data cache consistency, or
       when we have a write delegation, and know that our cache is still
       consistent.
     * Don't request NFSv4 post-op attributes on operations such as
       COMMIT, where there are no expected metadata updates.
     * Don't request NFSv4 directory post-op attributes in cases where
       the operations themselves already return change attribute
       updates: i.e. operations such as OPEN, CREATE, REMOVE, LINK and
       RENAME.
   - Speed up 'ls' and friends by using READDIR rather than READDIRPLUS
     if we detect no attempts to lookup filenames.
   - Improve the code sharing between NFSv2/v3 and v4 mounts
   - NFSv4.1 state management efficiency improvements
   - More patches in preparation for NFSv4/v4.1 migration functionality."

Fix trivial conflict in fs/nfs/nfs4proc.c that was due to the dcache
qstr name initialization changes (that made the length/hash a 64-bit
union)

* tag 'nfs-for-3.5-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (146 commits)
  NFSv4: Add debugging printks to state manager
  NFSv4: Map NFS4ERR_SHARE_DENIED into an EACCES error instead of EIO
  NFSv4: update_changeattr does not need to set NFS_INO_REVAL_PAGECACHE
  NFSv4.1: nfs4_reset_session should use nfs4_handle_reclaim_lease_error
  NFSv4.1: Handle other occurrences of NFS4ERR_CONN_NOT_BOUND_TO_SESSION
  NFSv4.1: Handle NFS4ERR_CONN_NOT_BOUND_TO_SESSION in the state manager
  NFSv4.1: Handle errors in nfs4_bind_conn_to_session
  NFSv4.1: nfs4_bind_conn_to_session should drain the session
  NFSv4.1: Don't clobber the seqid if exchange_id returns a confirmed clientid
  NFSv4.1: Add DESTROY_CLIENTID
  NFSv4.1: Ensure we use the correct credentials for bind_conn_to_session
  NFSv4.1: Ensure we use the correct credentials for session create/destroy
  NFSv4.1: Move NFSPROC4_CLNT_BIND_CONN_TO_SESSION to the end of the operations
  NFSv4.1: Handle NFS4ERR_SEQ_MISORDERED when confirming the lease
  NFSv4: When purging the lease, we must clear NFS4CLNT_LEASE_CONFIRM
  NFSv4: Clean up the error handling for nfs4_reclaim_lease
  NFSv4.1: Exchange ID must use GFP_NOFS allocation mode
  nfs41: Use BIND_CONN_TO_SESSION for CB_PATH_DOWN*
  nfs4.1: add BIND_CONN_TO_SESSION operation
  NFSv4.1 test the mdsthreshold hint parameters
  ...
parents 8f6576ad cc0a9843
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -29,9 +29,20 @@ config NFS_FS

	  If unsure, say N.

config NFS_V2
	bool "NFS client support for NFS version 2"
	depends on NFS_FS
	default y
	help
	  This option enables support for version 2 of the NFS protocol
	  (RFC 1094) in the kernel's NFS client.

	  If unsure, say Y.

config NFS_V3
	bool "NFS client support for NFS version 3"
	depends on NFS_FS
	default y
	help
	  This option enables support for version 3 of the NFS protocol
	  (RFC 1813) in the kernel's NFS client.
+3 −2
Original line number Diff line number Diff line
@@ -4,11 +4,12 @@

obj-$(CONFIG_NFS_FS) += nfs.o

nfs-y 			:= client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
			   direct.o pagelist.o proc.o read.o symlink.o unlink.o \
nfs-y 			:= client.o dir.o file.o getroot.o inode.o super.o \
			   direct.o pagelist.o read.o symlink.o unlink.o \
			   write.o namespace.o mount_clnt.o \
			   dns_resolve.o cache_lib.o
nfs-$(CONFIG_ROOT_NFS)	+= nfsroot.o
nfs-$(CONFIG_NFS_V2)	+= proc.o nfs2xdr.o
nfs-$(CONFIG_NFS_V3)	+= nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V3_ACL)	+= nfs3acl.o
nfs-$(CONFIG_NFS_V4)	+= nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
+49 −41
Original line number Diff line number Diff line
@@ -187,7 +187,6 @@ static void bl_end_io_read(struct bio *bio, int err)
	struct parallel_io *par = bio->bi_private;
	const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
	struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
	struct nfs_read_data *rdata = (struct nfs_read_data *)par->data;

	do {
		struct page *page = bvec->bv_page;
@@ -198,9 +197,12 @@ static void bl_end_io_read(struct bio *bio, int err)
			SetPageUptodate(page);
	} while (bvec >= bio->bi_io_vec);
	if (!uptodate) {
		if (!rdata->pnfs_error)
			rdata->pnfs_error = -EIO;
		pnfs_set_lo_fail(rdata->lseg);
		struct nfs_read_data *rdata = par->data;
		struct nfs_pgio_header *header = rdata->header;

		if (!header->pnfs_error)
			header->pnfs_error = -EIO;
		pnfs_set_lo_fail(header->lseg);
	}
	bio_put(bio);
	put_parallel(par);
@@ -221,7 +223,7 @@ bl_end_par_io_read(void *data, int unused)
{
	struct nfs_read_data *rdata = data;

	rdata->task.tk_status = rdata->pnfs_error;
	rdata->task.tk_status = rdata->header->pnfs_error;
	INIT_WORK(&rdata->task.u.tk_work, bl_read_cleanup);
	schedule_work(&rdata->task.u.tk_work);
}
@@ -229,6 +231,7 @@ bl_end_par_io_read(void *data, int unused)
static enum pnfs_try_status
bl_read_pagelist(struct nfs_read_data *rdata)
{
	struct nfs_pgio_header *header = rdata->header;
	int i, hole;
	struct bio *bio = NULL;
	struct pnfs_block_extent *be = NULL, *cow_read = NULL;
@@ -239,7 +242,7 @@ bl_read_pagelist(struct nfs_read_data *rdata)
	int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT;

	dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__,
	       rdata->npages, f_offset, (unsigned int)rdata->args.count);
	       rdata->pages.npages, f_offset, (unsigned int)rdata->args.count);

	par = alloc_parallel(rdata);
	if (!par)
@@ -249,17 +252,17 @@ bl_read_pagelist(struct nfs_read_data *rdata)

	isect = (sector_t) (f_offset >> SECTOR_SHIFT);
	/* Code assumes extents are page-aligned */
	for (i = pg_index; i < rdata->npages; i++) {
	for (i = pg_index; i < rdata->pages.npages; i++) {
		if (!extent_length) {
			/* We've used up the previous extent */
			bl_put_extent(be);
			bl_put_extent(cow_read);
			bio = bl_submit_bio(READ, bio);
			/* Get the next one */
			be = bl_find_get_extent(BLK_LSEG2EXT(rdata->lseg),
			be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg),
					     isect, &cow_read);
			if (!be) {
				rdata->pnfs_error = -EIO;
				header->pnfs_error = -EIO;
				goto out;
			}
			extent_length = be->be_length -
@@ -282,11 +285,12 @@ bl_read_pagelist(struct nfs_read_data *rdata)
			struct pnfs_block_extent *be_read;

			be_read = (hole && cow_read) ? cow_read : be;
			bio = bl_add_page_to_bio(bio, rdata->npages - i, READ,
			bio = bl_add_page_to_bio(bio, rdata->pages.npages - i,
						 READ,
						 isect, pages[i], be_read,
						 bl_end_io_read, par);
			if (IS_ERR(bio)) {
				rdata->pnfs_error = PTR_ERR(bio);
				header->pnfs_error = PTR_ERR(bio);
				bio = NULL;
				goto out;
			}
@@ -294,9 +298,9 @@ bl_read_pagelist(struct nfs_read_data *rdata)
		isect += PAGE_CACHE_SECTORS;
		extent_length -= PAGE_CACHE_SECTORS;
	}
	if ((isect << SECTOR_SHIFT) >= rdata->inode->i_size) {
	if ((isect << SECTOR_SHIFT) >= header->inode->i_size) {
		rdata->res.eof = 1;
		rdata->res.count = rdata->inode->i_size - f_offset;
		rdata->res.count = header->inode->i_size - f_offset;
	} else {
		rdata->res.count = (isect << SECTOR_SHIFT) - f_offset;
	}
@@ -345,7 +349,6 @@ static void bl_end_io_write_zero(struct bio *bio, int err)
	struct parallel_io *par = bio->bi_private;
	const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
	struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
	struct nfs_write_data *wdata = (struct nfs_write_data *)par->data;

	do {
		struct page *page = bvec->bv_page;
@@ -358,9 +361,12 @@ static void bl_end_io_write_zero(struct bio *bio, int err)
	} while (bvec >= bio->bi_io_vec);

	if (unlikely(!uptodate)) {
		if (!wdata->pnfs_error)
			wdata->pnfs_error = -EIO;
		pnfs_set_lo_fail(wdata->lseg);
		struct nfs_write_data *data = par->data;
		struct nfs_pgio_header *header = data->header;

		if (!header->pnfs_error)
			header->pnfs_error = -EIO;
		pnfs_set_lo_fail(header->lseg);
	}
	bio_put(bio);
	put_parallel(par);
@@ -370,12 +376,13 @@ static void bl_end_io_write(struct bio *bio, int err)
{
	struct parallel_io *par = bio->bi_private;
	const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
	struct nfs_write_data *wdata = (struct nfs_write_data *)par->data;
	struct nfs_write_data *data = par->data;
	struct nfs_pgio_header *header = data->header;

	if (!uptodate) {
		if (!wdata->pnfs_error)
			wdata->pnfs_error = -EIO;
		pnfs_set_lo_fail(wdata->lseg);
		if (!header->pnfs_error)
			header->pnfs_error = -EIO;
		pnfs_set_lo_fail(header->lseg);
	}
	bio_put(bio);
	put_parallel(par);
@@ -391,9 +398,9 @@ static void bl_write_cleanup(struct work_struct *work)
	dprintk("%s enter\n", __func__);
	task = container_of(work, struct rpc_task, u.tk_work);
	wdata = container_of(task, struct nfs_write_data, task);
	if (likely(!wdata->pnfs_error)) {
	if (likely(!wdata->header->pnfs_error)) {
		/* Marks for LAYOUTCOMMIT */
		mark_extents_written(BLK_LSEG2EXT(wdata->lseg),
		mark_extents_written(BLK_LSEG2EXT(wdata->header->lseg),
				     wdata->args.offset, wdata->args.count);
	}
	pnfs_ld_write_done(wdata);
@@ -404,12 +411,12 @@ static void bl_end_par_io_write(void *data, int num_se)
{
	struct nfs_write_data *wdata = data;

	if (unlikely(wdata->pnfs_error)) {
		bl_free_short_extents(&BLK_LSEG2EXT(wdata->lseg)->bl_inval,
	if (unlikely(wdata->header->pnfs_error)) {
		bl_free_short_extents(&BLK_LSEG2EXT(wdata->header->lseg)->bl_inval,
					num_se);
	}

	wdata->task.tk_status = wdata->pnfs_error;
	wdata->task.tk_status = wdata->header->pnfs_error;
	wdata->verf.committed = NFS_FILE_SYNC;
	INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup);
	schedule_work(&wdata->task.u.tk_work);
@@ -540,6 +547,7 @@ bl_find_get_zeroing_page(struct inode *inode, pgoff_t index,
static enum pnfs_try_status
bl_write_pagelist(struct nfs_write_data *wdata, int sync)
{
	struct nfs_pgio_header *header = wdata->header;
	int i, ret, npg_zero, pg_index, last = 0;
	struct bio *bio = NULL;
	struct pnfs_block_extent *be = NULL, *cow_read = NULL;
@@ -552,7 +560,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
	pgoff_t index;
	u64 temp;
	int npg_per_block =
	    NFS_SERVER(wdata->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT;
	    NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT;

	dprintk("%s enter, %Zu@%lld\n", __func__, count, offset);
	/* At this point, wdata->pages is a (sequential) list of nfs_pages.
@@ -566,7 +574,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
	/* At this point, have to be more careful with error handling */

	isect = (sector_t) ((offset & (long)PAGE_CACHE_MASK) >> SECTOR_SHIFT);
	be = bl_find_get_extent(BLK_LSEG2EXT(wdata->lseg), isect, &cow_read);
	be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg), isect, &cow_read);
	if (!be || !is_writable(be, isect)) {
		dprintk("%s no matching extents!\n", __func__);
		goto out_mds;
@@ -597,10 +605,10 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
			dprintk("%s zero %dth page: index %lu isect %llu\n",
				__func__, npg_zero, index,
				(unsigned long long)isect);
			page = bl_find_get_zeroing_page(wdata->inode, index,
			page = bl_find_get_zeroing_page(header->inode, index,
							cow_read);
			if (unlikely(IS_ERR(page))) {
				wdata->pnfs_error = PTR_ERR(page);
				header->pnfs_error = PTR_ERR(page);
				goto out;
			} else if (page == NULL)
				goto next_page;
@@ -612,7 +620,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
					__func__, ret);
				end_page_writeback(page);
				page_cache_release(page);
				wdata->pnfs_error = ret;
				header->pnfs_error = ret;
				goto out;
			}
			if (likely(!bl_push_one_short_extent(be->be_inval)))
@@ -620,11 +628,11 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
			else {
				end_page_writeback(page);
				page_cache_release(page);
				wdata->pnfs_error = -ENOMEM;
				header->pnfs_error = -ENOMEM;
				goto out;
			}
			/* FIXME: This should be done in bi_end_io */
			mark_extents_written(BLK_LSEG2EXT(wdata->lseg),
			mark_extents_written(BLK_LSEG2EXT(header->lseg),
					     page->index << PAGE_CACHE_SHIFT,
					     PAGE_CACHE_SIZE);

@@ -632,7 +640,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
						 isect, page, be,
						 bl_end_io_write_zero, par);
			if (IS_ERR(bio)) {
				wdata->pnfs_error = PTR_ERR(bio);
				header->pnfs_error = PTR_ERR(bio);
				bio = NULL;
				goto out;
			}
@@ -647,16 +655,16 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)

	/* Middle pages */
	pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT;
	for (i = pg_index; i < wdata->npages; i++) {
	for (i = pg_index; i < wdata->pages.npages; i++) {
		if (!extent_length) {
			/* We've used up the previous extent */
			bl_put_extent(be);
			bio = bl_submit_bio(WRITE, bio);
			/* Get the next one */
			be = bl_find_get_extent(BLK_LSEG2EXT(wdata->lseg),
			be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg),
					     isect, NULL);
			if (!be || !is_writable(be, isect)) {
				wdata->pnfs_error = -EINVAL;
				header->pnfs_error = -EINVAL;
				goto out;
			}
			if (be->be_state == PNFS_BLOCK_INVALID_DATA) {
@@ -664,7 +672,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
								be->be_inval)))
					par->bse_count++;
				else {
					wdata->pnfs_error = -ENOMEM;
					header->pnfs_error = -ENOMEM;
					goto out;
				}
			}
@@ -677,15 +685,15 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
			if (unlikely(ret)) {
				dprintk("%s bl_mark_sectors_init fail %d\n",
					__func__, ret);
				wdata->pnfs_error = ret;
				header->pnfs_error = ret;
				goto out;
			}
		}
		bio = bl_add_page_to_bio(bio, wdata->npages - i, WRITE,
		bio = bl_add_page_to_bio(bio, wdata->pages.npages - i, WRITE,
					 isect, pages[i], be,
					 bl_end_io_write, par);
		if (IS_ERR(bio)) {
			wdata->pnfs_error = PTR_ERR(bio);
			header->pnfs_error = PTR_ERR(bio);
			bio = NULL;
			goto out;
		}
+1 −1
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ nfs4_blk_decode_device(struct nfs_server *server,
	uint8_t *dataptr;
	DECLARE_WAITQUEUE(wq, current);
	int offset, len, i, rc;
	struct net *net = server->nfs_client->net;
	struct net *net = server->nfs_client->cl_net;
	struct nfs_net *nn = net_generic(net, nfs_net_id);
	struct bl_dev_msg *reply = &nn->bl_mount_reply;

+145 −123
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
{
	int ret = 0;
	struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);

	if (clp->rpc_ops->version != 4 || minorversion != 0)
		return ret;
@@ -90,7 +90,9 @@ static bool nfs4_disable_idmapping = true;
 * RPC cruft for NFS
 */
static const struct rpc_version *nfs_version[5] = {
#ifdef CONFIG_NFS_V2
	[2]			= &nfs_version2,
#endif
#ifdef CONFIG_NFS_V3
	[3]			= &nfs_version3,
#endif
@@ -129,6 +131,7 @@ const struct rpc_program nfsacl_program = {
#endif  /* CONFIG_NFS_V3_ACL */

struct nfs_client_initdata {
	unsigned long init_flags;
	const char *hostname;
	const struct sockaddr *addr;
	size_t addrlen;
@@ -172,7 +175,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
	clp->cl_rpcclient = ERR_PTR(-EINVAL);

	clp->cl_proto = cl_init->proto;
	clp->net = get_net(cl_init->net);
	clp->cl_net = get_net(cl_init->net);

#ifdef CONFIG_NFS_V4
	err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
@@ -182,7 +185,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
	spin_lock_init(&clp->cl_lock);
	INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
	rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
	clp->cl_boot_time = CURRENT_TIME;
	clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
	clp->cl_minorversion = cl_init->minorversion;
	clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
@@ -207,6 +209,7 @@ static void nfs4_shutdown_session(struct nfs_client *clp)
	if (nfs4_has_session(clp)) {
		nfs4_deviceid_purge_client(clp);
		nfs4_destroy_session(clp->cl_session);
		nfs4_destroy_clientid(clp);
	}

}
@@ -235,6 +238,9 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
		nfs_idmap_delete(clp);

	rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
	kfree(clp->cl_serverowner);
	kfree(clp->cl_serverscope);
	kfree(clp->cl_implid);
}

/* idr_remove_all is not needed as all id's are removed by nfs_put_client */
@@ -248,7 +254,7 @@ void nfs_cleanup_cb_ident_idr(struct net *net)
/* nfs_client_lock held */
static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
	struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);

	if (clp->cl_cb_ident)
		idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);
@@ -301,10 +307,8 @@ static void nfs_free_client(struct nfs_client *clp)
	if (clp->cl_machine_cred != NULL)
		put_rpccred(clp->cl_machine_cred);

	put_net(clp->net);
	put_net(clp->cl_net);
	kfree(clp->cl_hostname);
	kfree(clp->server_scope);
	kfree(clp->impl_id);
	kfree(clp);

	dprintk("<-- nfs_free_client()\n");
@@ -321,7 +325,7 @@ void nfs_put_client(struct nfs_client *clp)
		return;

	dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count));
	nn = net_generic(clp->net, nfs_net_id);
	nn = net_generic(clp->cl_net, nfs_net_id);

	if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) {
		list_del(&clp->cl_share_link);
@@ -456,6 +460,8 @@ static bool nfs4_cb_match_client(const struct sockaddr *addr,
	    clp->cl_cons_state == NFS_CS_SESSION_INITING))
		return false;

	smp_rmb();

	/* Match the version and minorversion */
	if (clp->rpc_ops->version != 4 ||
	    clp->cl_minorversion != minorversion)
@@ -504,6 +510,47 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
	return NULL;
}

static bool nfs_client_init_is_complete(const struct nfs_client *clp)
{
	return clp->cl_cons_state != NFS_CS_INITING;
}

int nfs_wait_client_init_complete(const struct nfs_client *clp)
{
	return wait_event_killable(nfs_client_active_wq,
			nfs_client_init_is_complete(clp));
}

/*
 * Found an existing client.  Make sure it's ready before returning.
 */
static struct nfs_client *
nfs_found_client(const struct nfs_client_initdata *cl_init,
		 struct nfs_client *clp)
{
	int error;

	error = nfs_wait_client_init_complete(clp);
	if (error < 0) {
		nfs_put_client(clp);
		return ERR_PTR(-ERESTARTSYS);
	}

	if (clp->cl_cons_state < NFS_CS_READY) {
		error = clp->cl_cons_state;
		nfs_put_client(clp);
		return ERR_PTR(error);
	}

	smp_rmb();

	BUG_ON(clp->cl_cons_state != NFS_CS_READY);

	dprintk("<-- %s found nfs_client %p for %s\n",
		__func__, clp, cl_init->hostname ?: "");
	return clp;
}

/*
 * Look up a client by IP address and protocol version
 * - creates a new record if one doesn't yet exist
@@ -512,11 +559,9 @@ static struct nfs_client *
nfs_get_client(const struct nfs_client_initdata *cl_init,
	       const struct rpc_timeout *timeparms,
	       const char *ip_addr,
	       rpc_authflavor_t authflavour,
	       int noresvport)
	       rpc_authflavor_t authflavour)
{
	struct nfs_client *clp, *new = NULL;
	int error;
	struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);

	dprintk("--> nfs_get_client(%s,v%u)\n",
@@ -527,60 +572,29 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
		spin_lock(&nn->nfs_client_lock);

		clp = nfs_match_client(cl_init);
		if (clp)
			goto found_client;
		if (clp) {
			spin_unlock(&nn->nfs_client_lock);
			if (new)
			goto install_client;
				nfs_free_client(new);
			return nfs_found_client(cl_init, clp);
		}
		if (new) {
			list_add(&new->cl_share_link, &nn->nfs_client_list);
			spin_unlock(&nn->nfs_client_lock);
			new->cl_flags = cl_init->init_flags;
			return cl_init->rpc_ops->init_client(new,
						timeparms, ip_addr,
						authflavour);
		}

		spin_unlock(&nn->nfs_client_lock);

		new = nfs_alloc_client(cl_init);
	} while (!IS_ERR(new));

	dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new));
	dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
		cl_init->hostname ?: "", PTR_ERR(new));
	return new;

	/* install a new client and return with it unready */
install_client:
	clp = new;
	list_add(&clp->cl_share_link, &nn->nfs_client_list);
	spin_unlock(&nn->nfs_client_lock);

	error = cl_init->rpc_ops->init_client(clp, timeparms, ip_addr,
					      authflavour, noresvport);
	if (error < 0) {
		nfs_put_client(clp);
		return ERR_PTR(error);
	}
	dprintk("--> nfs_get_client() = %p [new]\n", clp);
	return clp;

	/* found an existing client
	 * - make sure it's ready before returning
	 */
found_client:
	spin_unlock(&nn->nfs_client_lock);

	if (new)
		nfs_free_client(new);

	error = wait_event_killable(nfs_client_active_wq,
				clp->cl_cons_state < NFS_CS_INITING);
	if (error < 0) {
		nfs_put_client(clp);
		return ERR_PTR(-ERESTARTSYS);
	}

	if (clp->cl_cons_state < NFS_CS_READY) {
		error = clp->cl_cons_state;
		nfs_put_client(clp);
		return ERR_PTR(error);
	}

	BUG_ON(clp->cl_cons_state != NFS_CS_READY);

	dprintk("--> nfs_get_client() = %p [share]\n", clp);
	return clp;
}

/*
@@ -588,26 +602,11 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
 */
void nfs_mark_client_ready(struct nfs_client *clp, int state)
{
	smp_wmb();
	clp->cl_cons_state = state;
	wake_up_all(&nfs_client_active_wq);
}

/*
 * With sessions, the client is not marked ready until after a
 * successful EXCHANGE_ID and CREATE_SESSION.
 *
 * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
 * other versions of NFS can be tried.
 */
int nfs4_check_client_ready(struct nfs_client *clp)
{
	if (!nfs4_has_session(clp))
		return 0;
	if (clp->cl_cons_state < NFS_CS_READY)
		return -EPROTONOSUPPORT;
	return 0;
}

/*
 * Initialise the timeout values for a connection
 */
@@ -654,12 +653,11 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
 */
static int nfs_create_rpc_client(struct nfs_client *clp,
				 const struct rpc_timeout *timeparms,
				 rpc_authflavor_t flavor,
				 int discrtry, int noresvport)
				 rpc_authflavor_t flavor)
{
	struct rpc_clnt		*clnt = NULL;
	struct rpc_create_args args = {
		.net		= clp->net,
		.net		= clp->cl_net,
		.protocol	= clp->cl_proto,
		.address	= (struct sockaddr *)&clp->cl_addr,
		.addrsize	= clp->cl_addrlen,
@@ -670,9 +668,9 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
		.authflavor	= flavor,
	};

	if (discrtry)
	if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
		args.flags |= RPC_CLNT_CREATE_DISCRTRY;
	if (noresvport)
	if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))
		args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;

	if (!IS_ERR(clp->cl_rpcclient))
@@ -713,7 +711,7 @@ static int nfs_start_lockd(struct nfs_server *server)
		.nfs_version	= clp->rpc_ops->version,
		.noresvport	= server->flags & NFS_MOUNT_NORESVPORT ?
					1 : 0,
		.net		= clp->net,
		.net		= clp->cl_net,
	};

	if (nlm_init.nfs_version > 3)
@@ -805,36 +803,43 @@ static int nfs_init_server_rpcclient(struct nfs_server *server,
	return 0;
}

/*
 * Initialise an NFS2 or NFS3 client
/**
 * nfs_init_client - Initialise an NFS2 or NFS3 client
 *
 * @clp: nfs_client to initialise
 * @timeparms: timeout parameters for underlying RPC transport
 * @ip_addr: IP presentation address (not used)
 * @authflavor: authentication flavor for underlying RPC transport
 *
 * Returns pointer to an NFS client, or an ERR_PTR value.
 */
int nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms,
		    const char *ip_addr, rpc_authflavor_t authflavour,
		    int noresvport)
struct nfs_client *nfs_init_client(struct nfs_client *clp,
		    const struct rpc_timeout *timeparms,
		    const char *ip_addr, rpc_authflavor_t authflavour)
{
	int error;

	if (clp->cl_cons_state == NFS_CS_READY) {
		/* the client is already initialised */
		dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp);
		return 0;
		return clp;
	}

	/*
	 * Create a client RPC handle for doing FSSTAT with UNIX auth only
	 * - RFC 2623, sec 2.3.2
	 */
	error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX,
				      0, noresvport);
	error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX);
	if (error < 0)
		goto error;
	nfs_mark_client_ready(clp, NFS_CS_READY);
	return 0;
	return clp;

error:
	nfs_mark_client_ready(clp, error);
	nfs_put_client(clp);
	dprintk("<-- nfs_init_client() = xerror %d\n", error);
	return error;
	return ERR_PTR(error);
}

/*
@@ -847,7 +852,7 @@ static int nfs_init_server(struct nfs_server *server,
		.hostname = data->nfs_server.hostname,
		.addr = (const struct sockaddr *)&data->nfs_server.address,
		.addrlen = data->nfs_server.addrlen,
		.rpc_ops = &nfs_v2_clientops,
		.rpc_ops = NULL,
		.proto = data->nfs_server.protocol,
		.net = data->net,
	};
@@ -857,17 +862,28 @@ static int nfs_init_server(struct nfs_server *server,

	dprintk("--> nfs_init_server()\n");

	switch (data->version) {
#ifdef CONFIG_NFS_V2
	case 2:
		cl_init.rpc_ops = &nfs_v2_clientops;
		break;
#endif
#ifdef CONFIG_NFS_V3
	if (data->version == 3)
	case 3:
		cl_init.rpc_ops = &nfs_v3_clientops;
		break;
#endif
	default:
		return -EPROTONOSUPPORT;
	}

	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
			data->timeo, data->retrans);
	if (data->flags & NFS_MOUNT_NORESVPORT)
		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);

	/* Allocate or find a client reference we can use */
	clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX,
			     data->flags & NFS_MOUNT_NORESVPORT);
	clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX);
	if (IS_ERR(clp)) {
		dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));
		return PTR_ERR(clp);
@@ -880,7 +896,7 @@ static int nfs_init_server(struct nfs_server *server,
	server->options = data->options;
	server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
		NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
		NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
		NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR;

	if (data->rsize)
		server->rsize = nfs_block_size(data->rsize, NULL);
@@ -1048,7 +1064,7 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
static void nfs_server_insert_lists(struct nfs_server *server)
{
	struct nfs_client *clp = server->nfs_client;
	struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);

	spin_lock(&nn->nfs_client_lock);
	list_add_tail_rcu(&server->client_link, &clp->cl_superblocks);
@@ -1065,7 +1081,7 @@ static void nfs_server_remove_lists(struct nfs_server *server)

	if (clp == NULL)
		return;
	nn = net_generic(clp->net, nfs_net_id);
	nn = net_generic(clp->cl_net, nfs_net_id);
	spin_lock(&nn->nfs_client_lock);
	list_del_rcu(&server->client_link);
	if (list_empty(&clp->cl_superblocks))
@@ -1333,21 +1349,27 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
		 * so that the client back channel can find the
		 * nfs_client struct
		 */
		clp->cl_cons_state = NFS_CS_SESSION_INITING;
		nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
	}
#endif /* CONFIG_NFS_V4_1 */

	return nfs4_init_callback(clp);
}

/*
 * Initialise an NFS4 client record
/**
 * nfs4_init_client - Initialise an NFS4 client record
 *
 * @clp: nfs_client to initialise
 * @timeparms: timeout parameters for underlying RPC transport
 * @ip_addr: callback IP address in presentation format
 * @authflavor: authentication flavor for underlying RPC transport
 *
 * Returns pointer to an NFS client, or an ERR_PTR value.
 */
int nfs4_init_client(struct nfs_client *clp,
struct nfs_client *nfs4_init_client(struct nfs_client *clp,
				    const struct rpc_timeout *timeparms,
				    const char *ip_addr,
		     rpc_authflavor_t authflavour,
		     int noresvport)
				    rpc_authflavor_t authflavour)
{
	char buf[INET6_ADDRSTRLEN + 1];
	int error;
@@ -1355,14 +1377,14 @@ int nfs4_init_client(struct nfs_client *clp,
	if (clp->cl_cons_state == NFS_CS_READY) {
		/* the client is initialised already */
		dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
		return 0;
		return clp;
	}

	/* Check NFS protocol revision and initialize RPC op vector */
	clp->rpc_ops = &nfs_v4_clientops;

	error = nfs_create_rpc_client(clp, timeparms, authflavour,
				      1, noresvport);
	__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
	error = nfs_create_rpc_client(clp, timeparms, authflavour);
	if (error < 0)
		goto error;

@@ -1395,12 +1417,13 @@ int nfs4_init_client(struct nfs_client *clp,

	if (!nfs4_has_session(clp))
		nfs_mark_client_ready(clp, NFS_CS_READY);
	return 0;
	return clp;

error:
	nfs_mark_client_ready(clp, error);
	nfs_put_client(clp);
	dprintk("<-- nfs4_init_client() = xerror %d\n", error);
	return error;
	return ERR_PTR(error);
}

/*
@@ -1429,9 +1452,11 @@ static int nfs4_set_client(struct nfs_server *server,

	dprintk("--> nfs4_set_client()\n");

	if (server->flags & NFS_MOUNT_NORESVPORT)
		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);

	/* Allocate or find a client reference we can use */
	clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour,
			     server->flags & NFS_MOUNT_NORESVPORT);
	clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
	if (IS_ERR(clp)) {
		error = PTR_ERR(clp);
		goto error;
@@ -1465,8 +1490,8 @@ static int nfs4_set_client(struct nfs_server *server,
 * the MDS.
 */
struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
		const struct sockaddr *ds_addr,
		int ds_addrlen, int ds_proto)
		const struct sockaddr *ds_addr, int ds_addrlen,
		int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
{
	struct nfs_client_initdata cl_init = {
		.addr = ds_addr,
@@ -1474,14 +1499,9 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
		.rpc_ops = &nfs_v4_clientops,
		.proto = ds_proto,
		.minorversion = mds_clp->cl_minorversion,
		.net = mds_clp->net,
	};
	struct rpc_timeout ds_timeout = {
		.to_initval = 15 * HZ,
		.to_maxval = 15 * HZ,
		.to_retries = 1,
		.to_exponential = 1,
		.net = mds_clp->cl_net,
	};
	struct rpc_timeout ds_timeout;
	struct nfs_client *clp;

	/*
@@ -1489,8 +1509,9 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
	 * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
	 * (section 13.1 RFC 5661).
	 */
	nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
	clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
			     mds_clp->cl_rpcclient->cl_auth->au_flavor, 0);
			     mds_clp->cl_rpcclient->cl_auth->au_flavor);

	dprintk("<-- %s %p\n", __func__, clp);
	return clp;
@@ -1701,7 +1722,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
				rpc_protocol(parent_server->client),
				parent_server->client->cl_timeout,
				parent_client->cl_mvops->minor_version,
				parent_client->net);
				parent_client->cl_net);
	if (error < 0)
		goto error;

@@ -1805,6 +1826,7 @@ void nfs_clients_init(struct net *net)
	idr_init(&nn->cb_ident_idr);
#endif
	spin_lock_init(&nn->nfs_client_lock);
	nn->boot_time = CURRENT_TIME;
}

#ifdef CONFIG_PROC_FS
Loading