nfs4proc.c 168 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/*
 *  fs/nfs/nfs4proc.c
 *
 *  Client-side procedure declarations for NFSv4.
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Kendrick Smith <kmsmith@umich.edu>
 *  Andy Adamson   <andros@umich.edu>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/string.h>
42
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
43
#include <linux/sunrpc/clnt.h>
44
#include <linux/sunrpc/gss_api.h>
Linus Torvalds's avatar
Linus Torvalds committed
45 46 47 48
#include <linux/nfs.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
49
#include <linux/nfs_mount.h>
Linus Torvalds's avatar
Linus Torvalds committed
50
#include <linux/namei.h>
51
#include <linux/mount.h>
Benny Halevy's avatar
Benny Halevy committed
52
#include <linux/module.h>
53
#include <linux/sunrpc/bc_xprt.h>
54
#include <linux/xattr.h>
55
#include <linux/utsname.h>
Linus Torvalds's avatar
Linus Torvalds committed
56

57
#include "nfs4_fs.h"
Linus Torvalds's avatar
Linus Torvalds committed
58
#include "delegation.h"
59
#include "internal.h"
60
#include "iostat.h"
Andy Adamson's avatar
Andy Adamson committed
61
#include "callback.h"
62
#include "pnfs.h"
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65

#define NFSDBG_FACILITY		NFSDBG_PROC

66
#define NFS4_POLL_RETRY_MIN	(HZ/10)
Linus Torvalds's avatar
Linus Torvalds committed
67 68
#define NFS4_POLL_RETRY_MAX	(15*HZ)

69 70
#define NFS4_MAX_LOOP_ON_RECOVER (10)

71
struct nfs4_opendata;
72
static int _nfs4_proc_open(struct nfs4_opendata *data);
73
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
Linus Torvalds's avatar
Linus Torvalds committed
74
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
75
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
76
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
77 78 79
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs4_state *state);
80 81 82 83
#ifdef CONFIG_NFS_V4_1
static int nfs41_test_stateid(struct nfs_server *, struct nfs4_state *);
static int nfs41_free_stateid(struct nfs_server *, struct nfs4_state *);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
84
/* Prevent leaks of NFSv4 errors into userland */
85
static int nfs4_map_errors(int err)
Linus Torvalds's avatar
Linus Torvalds committed
86
{
87 88 89 90 91
	if (err >= -1000)
		return err;
	switch (err) {
	case -NFS4ERR_RESOURCE:
		return -EREMOTEIO;
92 93
	case -NFS4ERR_WRONGSEC:
		return -EPERM;
94 95 96
	case -NFS4ERR_BADOWNER:
	case -NFS4ERR_BADNAME:
		return -EINVAL;
97
	default:
Linus Torvalds's avatar
Linus Torvalds committed
98
		dprintk("%s could not handle NFSv4 error %d\n",
99
				__func__, -err);
100
		break;
Linus Torvalds's avatar
Linus Torvalds committed
101
	}
102
	return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
}

/*
 * This is our standard bitmap for GETATTR requests.
 */
const u32 nfs4_fattr_bitmap[2] = {
	FATTR4_WORD0_TYPE
	| FATTR4_WORD0_CHANGE
	| FATTR4_WORD0_SIZE
	| FATTR4_WORD0_FSID
	| FATTR4_WORD0_FILEID,
	FATTR4_WORD1_MODE
	| FATTR4_WORD1_NUMLINKS
	| FATTR4_WORD1_OWNER
	| FATTR4_WORD1_OWNER_GROUP
	| FATTR4_WORD1_RAWDEV
	| FATTR4_WORD1_SPACE_USED
	| FATTR4_WORD1_TIME_ACCESS
	| FATTR4_WORD1_TIME_METADATA
	| FATTR4_WORD1_TIME_MODIFY
};

const u32 nfs4_statfs_bitmap[2] = {
	FATTR4_WORD0_FILES_AVAIL
	| FATTR4_WORD0_FILES_FREE
	| FATTR4_WORD0_FILES_TOTAL,
	FATTR4_WORD1_SPACE_AVAIL
	| FATTR4_WORD1_SPACE_FREE
	| FATTR4_WORD1_SPACE_TOTAL
};

134
const u32 nfs4_pathconf_bitmap[2] = {
Linus Torvalds's avatar
Linus Torvalds committed
135 136 137 138 139
	FATTR4_WORD0_MAXLINK
	| FATTR4_WORD0_MAXNAME,
	0
};

140
const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
Linus Torvalds's avatar
Linus Torvalds committed
141 142 143
			| FATTR4_WORD0_MAXREAD
			| FATTR4_WORD0_MAXWRITE
			| FATTR4_WORD0_LEASE_TIME,
144
			FATTR4_WORD1_TIME_DELTA
145 146
			| FATTR4_WORD1_FS_LAYOUT_TYPES,
			FATTR4_WORD2_LAYOUT_BLKSIZE
Linus Torvalds's avatar
Linus Torvalds committed
147 148
};

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
const u32 nfs4_fs_locations_bitmap[2] = {
	FATTR4_WORD0_TYPE
	| FATTR4_WORD0_CHANGE
	| FATTR4_WORD0_SIZE
	| FATTR4_WORD0_FSID
	| FATTR4_WORD0_FILEID
	| FATTR4_WORD0_FS_LOCATIONS,
	FATTR4_WORD1_MODE
	| FATTR4_WORD1_NUMLINKS
	| FATTR4_WORD1_OWNER
	| FATTR4_WORD1_OWNER_GROUP
	| FATTR4_WORD1_RAWDEV
	| FATTR4_WORD1_SPACE_USED
	| FATTR4_WORD1_TIME_ACCESS
	| FATTR4_WORD1_TIME_METADATA
	| FATTR4_WORD1_TIME_MODIFY
	| FATTR4_WORD1_MOUNTED_ON_FILEID
};

168
static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
Linus Torvalds's avatar
Linus Torvalds committed
169 170
		struct nfs4_readdir_arg *readdir)
{
171
	__be32 *start, *p;
Linus Torvalds's avatar
Linus Torvalds committed
172 173 174

	BUG_ON(readdir->count < 80);
	if (cookie > 2) {
175
		readdir->cookie = cookie;
Linus Torvalds's avatar
Linus Torvalds committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
		memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
		return;
	}

	readdir->cookie = 0;
	memset(&readdir->verifier, 0, sizeof(readdir->verifier));
	if (cookie == 2)
		return;
	
	/*
	 * NFSv4 servers do not return entries for '.' and '..'
	 * Therefore, we fake these entries here.  We let '.'
	 * have cookie 0 and '..' have cookie 1.  Note that
	 * when talking to the server, we always send cookie 0
	 * instead of 1 or 2.
	 */
192
	start = p = kmap_atomic(*readdir->pages, KM_USER0);
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197 198 199 200 201 202 203
	
	if (cookie == 0) {
		*p++ = xdr_one;                                  /* next */
		*p++ = xdr_zero;                   /* cookie, first word */
		*p++ = xdr_one;                   /* cookie, second word */
		*p++ = xdr_one;                             /* entry len */
		memcpy(p, ".\0\0\0", 4);                        /* entry */
		p++;
		*p++ = xdr_one;                         /* bitmap length */
		*p++ = htonl(FATTR4_WORD0_FILEID);             /* bitmap */
		*p++ = htonl(8);              /* attribute buffer length */
204
		p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode));
Linus Torvalds's avatar
Linus Torvalds committed
205 206 207 208 209 210 211 212 213 214 215
	}
	
	*p++ = xdr_one;                                  /* next */
	*p++ = xdr_zero;                   /* cookie, first word */
	*p++ = xdr_two;                   /* cookie, second word */
	*p++ = xdr_two;                             /* entry len */
	memcpy(p, "..\0\0", 4);                         /* entry */
	p++;
	*p++ = xdr_one;                         /* bitmap length */
	*p++ = htonl(FATTR4_WORD0_FILEID);             /* bitmap */
	*p++ = htonl(8);              /* attribute buffer length */
216
	p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode));
Linus Torvalds's avatar
Linus Torvalds committed
217 218 219 220 221 222

	readdir->pgbase = (char *)p - (char *)start;
	readdir->count -= readdir->pgbase;
	kunmap_atomic(start, KM_USER0);
}

223 224 225 226 227 228
static int nfs4_wait_clnt_recover(struct nfs_client *clp)
{
	int res;

	might_sleep();

229
	res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
230
			nfs_wait_bit_killable, TASK_KILLABLE);
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	return res;
}

static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
{
	int res = 0;

	might_sleep();

	if (*timeout <= 0)
		*timeout = NFS4_POLL_RETRY_MIN;
	if (*timeout > NFS4_POLL_RETRY_MAX)
		*timeout = NFS4_POLL_RETRY_MAX;
	schedule_timeout_killable(*timeout);
	if (fatal_signal_pending(current))
		res = -ERESTARTSYS;
	*timeout <<= 1;
	return res;
}

/* This is the error handling routine for processes that are allowed
 * to sleep.
 */
254
static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
255 256
{
	struct nfs_client *clp = server->nfs_client;
257
	struct nfs4_state *state = exception->state;
258 259 260 261 262 263
	int ret = errorcode;

	exception->retry = 0;
	switch(errorcode) {
		case 0:
			return 0;
264 265 266 267 268
		case -NFS4ERR_ADMIN_REVOKED:
		case -NFS4ERR_BAD_STATEID:
		case -NFS4ERR_OPENMODE:
			if (state == NULL)
				break;
269 270
			nfs4_schedule_stateid_recovery(server, state);
			goto wait_on_recovery;
271 272 273
		case -NFS4ERR_EXPIRED:
			if (state != NULL)
				nfs4_schedule_stateid_recovery(server, state);
274
		case -NFS4ERR_STALE_STATEID:
275
		case -NFS4ERR_STALE_CLIENTID:
276 277
			nfs4_schedule_lease_recovery(clp);
			goto wait_on_recovery;
278
#if defined(CONFIG_NFS_V4_1)
279 280 281 282 283 284 285 286 287
		case -NFS4ERR_BADSESSION:
		case -NFS4ERR_BADSLOT:
		case -NFS4ERR_BAD_HIGH_SLOT:
		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
		case -NFS4ERR_DEADSESSION:
		case -NFS4ERR_SEQ_FALSE_RETRY:
		case -NFS4ERR_SEQ_MISORDERED:
			dprintk("%s ERROR: %d Reset session\n", __func__,
				errorcode);
288
			nfs4_schedule_session_recovery(clp->cl_session);
289
			exception->retry = 1;
290
			break;
291
#endif /* defined(CONFIG_NFS_V4_1) */
292
		case -NFS4ERR_FILE_OPEN:
293 294 295 296 297 298 299
			if (exception->timeout > HZ) {
				/* We have retried a decent amount, time to
				 * fail
				 */
				ret = -EBUSY;
				break;
			}
300 301
		case -NFS4ERR_GRACE:
		case -NFS4ERR_DELAY:
302
		case -EKEYEXPIRED:
303 304 305
			ret = nfs4_delay(server->client, &exception->timeout);
			if (ret != 0)
				break;
306
		case -NFS4ERR_RETRY_UNCACHED_REP:
307 308
		case -NFS4ERR_OLD_STATEID:
			exception->retry = 1;
309 310 311 312 313 314 315 316 317 318 319 320 321
			break;
		case -NFS4ERR_BADOWNER:
			/* The following works around a Linux server bug! */
		case -NFS4ERR_BADNAME:
			if (server->caps & NFS_CAP_UIDGID_NOMAP) {
				server->caps &= ~NFS_CAP_UIDGID_NOMAP;
				exception->retry = 1;
				printk(KERN_WARNING "NFS: v4 server %s "
						"does not accept raw "
						"uid/gids. "
						"Reenabling the idmapper.\n",
						server->nfs_client->cl_hostname);
			}
322 323 324
	}
	/* We failed to handle the error */
	return nfs4_map_errors(ret);
325
wait_on_recovery:
326 327 328 329
	ret = nfs4_wait_clnt_recover(clp);
	if (ret == 0)
		exception->retry = 1;
	return ret;
330 331 332
}


333
static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp)
Linus Torvalds's avatar
Linus Torvalds committed
334 335 336 337 338 339 340
{
	spin_lock(&clp->cl_lock);
	if (time_before(clp->cl_last_renewal,timestamp))
		clp->cl_last_renewal = timestamp;
	spin_unlock(&clp->cl_lock);
}

341 342 343 344 345
static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{
	do_renew_lease(server->nfs_client, timestamp);
}

Andy Adamson's avatar
Andy Adamson committed
346 347
#if defined(CONFIG_NFS_V4_1)

Andy Adamson's avatar
Andy Adamson committed
348 349 350 351 352 353 354 355 356 357 358 359
/*
 * nfs4_free_slot - free a slot and efficiently update slot table.
 *
 * freeing a slot is trivially done by clearing its respective bit
 * in the bitmap.
 * If the freed slotid equals highest_used_slotid we want to update it
 * so that the server would be able to size down the slot table if needed,
 * otherwise we know that the highest_used_slotid is still in use.
 * When updating highest_used_slotid there may be "holes" in the bitmap
 * so we need to scan down from highest_used_slotid to 0 looking for the now
 * highest slotid in use.
 * If none found, highest_used_slotid is set to -1.
360 361
 *
 * Must be called while holding tbl->slot_tbl_lock
Andy Adamson's avatar
Andy Adamson committed
362 363
 */
static void
364
nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *free_slot)
Andy Adamson's avatar
Andy Adamson committed
365
{
366
	int free_slotid = free_slot - tbl->slots;
Andy Adamson's avatar
Andy Adamson committed
367 368
	int slotid = free_slotid;

369
	BUG_ON(slotid < 0 || slotid >= NFS4_MAX_SLOT_TABLE);
Andy Adamson's avatar
Andy Adamson committed
370 371 372 373 374 375
	/* clear used bit in bitmap */
	__clear_bit(slotid, tbl->used_slots);

	/* update highest_used_slotid when it is freed */
	if (slotid == tbl->highest_used_slotid) {
		slotid = find_last_bit(tbl->used_slots, tbl->max_slots);
Trond Myklebust's avatar
Trond Myklebust committed
376
		if (slotid < tbl->max_slots)
Andy Adamson's avatar
Andy Adamson committed
377 378 379 380 381 382 383 384
			tbl->highest_used_slotid = slotid;
		else
			tbl->highest_used_slotid = -1;
	}
	dprintk("%s: free_slotid %u highest_used_slotid %d\n", __func__,
		free_slotid, tbl->highest_used_slotid);
}

385
/*
386
 * Signal state manager thread if session fore channel is drained
387
 */
388
static void nfs4_check_drain_fc_complete(struct nfs4_session *ses)
389
{
390 391
	struct rpc_task *task;

392
	if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state)) {
393 394 395
		task = rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq);
		if (task)
			rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
396 397 398 399 400 401
		return;
	}

	if (ses->fc_slot_table.highest_used_slotid != -1)
		return;

402 403 404 405 406 407 408 409 410 411 412 413 414 415
	dprintk("%s COMPLETE: Session Fore Channel Drained\n", __func__);
	complete(&ses->fc_slot_table.complete);
}

/*
 * Signal state manager thread if session back channel is drained
 */
void nfs4_check_drain_bc_complete(struct nfs4_session *ses)
{
	if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state) ||
	    ses->bc_slot_table.highest_used_slotid != -1)
		return;
	dprintk("%s COMPLETE: Session Back Channel Drained\n", __func__);
	complete(&ses->bc_slot_table.complete);
416 417
}

418
static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
Andy Adamson's avatar
Andy Adamson committed
419 420 421
{
	struct nfs4_slot_table *tbl;

422
	tbl = &res->sr_session->fc_slot_table;
423
	if (!res->sr_slot) {
Andy Adamson's avatar
Andy Adamson committed
424 425
		/* just wake up the next guy waiting since
		 * we may have not consumed a slot after all */
Andy Adamson's avatar
Andy Adamson committed
426
		dprintk("%s: No slot\n", __func__);
427
		return;
Andy Adamson's avatar
Andy Adamson committed
428
	}
429

430
	spin_lock(&tbl->slot_tbl_lock);
431
	nfs4_free_slot(tbl, res->sr_slot);
432
	nfs4_check_drain_fc_complete(res->sr_session);
433
	spin_unlock(&tbl->slot_tbl_lock);
434
	res->sr_slot = NULL;
Andy Adamson's avatar
Andy Adamson committed
435 436
}

437
static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
Andy Adamson's avatar
Andy Adamson committed
438 439
{
	unsigned long timestamp;
440
	struct nfs_client *clp;
Andy Adamson's avatar
Andy Adamson committed
441 442 443 444 445 446 447 448 449 450

	/*
	 * sr_status remains 1 if an RPC level error occurred. The server
	 * may or may not have processed the sequence operation..
	 * Proceed as if the server received and processed the sequence
	 * operation.
	 */
	if (res->sr_status == 1)
		res->sr_status = NFS_OK;

451 452
	/* don't increment the sequence number if the task wasn't sent */
	if (!RPC_WAS_SENT(task))
Andy Adamson's avatar
Andy Adamson committed
453 454
		goto out;

Andy Adamson's avatar
Andy Adamson committed
455
	/* Check the SEQUENCE operation status */
456 457
	switch (res->sr_status) {
	case 0:
Andy Adamson's avatar
Andy Adamson committed
458
		/* Update the slot's sequence and clientid lease timer */
459
		++res->sr_slot->seq_nr;
Andy Adamson's avatar
Andy Adamson committed
460
		timestamp = res->sr_renewal_time;
461
		clp = res->sr_session->clp;
462
		do_renew_lease(clp, timestamp);
463
		/* Check sequence flags */
464 465
		if (res->sr_status_flags != 0)
			nfs4_schedule_lease_recovery(clp);
466 467 468 469 470 471
		break;
	case -NFS4ERR_DELAY:
		/* The server detected a resend of the RPC call and
		 * returned NFS4ERR_DELAY as per Section 2.10.6.2
		 * of RFC5661.
		 */
472
		dprintk("%s: slot=%td seq=%d: Operation in progress\n",
473 474 475
			__func__,
			res->sr_slot - res->sr_session->fc_slot_table.slots,
			res->sr_slot->seq_nr);
476 477 478
		goto out_retry;
	default:
		/* Just update the slot sequence no. */
479
		++res->sr_slot->seq_nr;
Andy Adamson's avatar
Andy Adamson committed
480 481 482 483
	}
out:
	/* The session may be reset by one of the error handlers. */
	dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
484
	nfs41_sequence_free_slot(res);
485 486
	return 1;
out_retry:
487
	if (!rpc_restart_call(task))
488 489 490
		goto out;
	rpc_delay(task, NFS4_POLL_RETRY_MAX);
	return 0;
Andy Adamson's avatar
Andy Adamson committed
491 492
}

493 494
static int nfs4_sequence_done(struct rpc_task *task,
			       struct nfs4_sequence_res *res)
495
{
496 497 498
	if (res->sr_session == NULL)
		return 1;
	return nfs41_sequence_done(task, res);
499 500
}

Benny Halevy's avatar
Benny Halevy committed
501 502 503 504 505 506 507
/*
 * nfs4_find_slot - efficiently look for a free slot
 *
 * nfs4_find_slot looks for an unset bit in the used_slots bitmap.
 * If found, we mark the slot as used, update the highest_used_slotid,
 * and respectively set up the sequence operation args.
 * The slot number is returned if found, or NFS4_MAX_SLOT_TABLE otherwise.
Andy Adamson's avatar
Andy Adamson committed
508 509
 *
 * Note: must be called with under the slot_tbl_lock.
Benny Halevy's avatar
Benny Halevy committed
510 511
 */
static u8
512
nfs4_find_slot(struct nfs4_slot_table *tbl)
Benny Halevy's avatar
Benny Halevy committed
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
{
	int slotid;
	u8 ret_id = NFS4_MAX_SLOT_TABLE;
	BUILD_BUG_ON((u8)NFS4_MAX_SLOT_TABLE != (int)NFS4_MAX_SLOT_TABLE);

	dprintk("--> %s used_slots=%04lx highest_used=%d max_slots=%d\n",
		__func__, tbl->used_slots[0], tbl->highest_used_slotid,
		tbl->max_slots);
	slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots);
	if (slotid >= tbl->max_slots)
		goto out;
	__set_bit(slotid, tbl->used_slots);
	if (slotid > tbl->highest_used_slotid)
		tbl->highest_used_slotid = slotid;
	ret_id = slotid;
out:
	dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n",
		__func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id);
	return ret_id;
}

Andy Adamson's avatar
Andy Adamson committed
534
int nfs41_setup_sequence(struct nfs4_session *session,
Andy Adamson's avatar
Andy Adamson committed
535 536 537 538 539
				struct nfs4_sequence_args *args,
				struct nfs4_sequence_res *res,
				int cache_reply,
				struct rpc_task *task)
{
Andy Adamson's avatar
Andy Adamson committed
540 541 542 543 544
	struct nfs4_slot *slot;
	struct nfs4_slot_table *tbl;
	u8 slotid;

	dprintk("--> %s\n", __func__);
Andy Adamson's avatar
Andy Adamson committed
545
	/* slot already allocated? */
546
	if (res->sr_slot != NULL)
Andy Adamson's avatar
Andy Adamson committed
547 548
		return 0;

Andy Adamson's avatar
Andy Adamson committed
549 550 551
	tbl = &session->fc_slot_table;

	spin_lock(&tbl->slot_tbl_lock);
552
	if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) &&
553
	    !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) {
554 555 556 557
		/*
		 * The state manager will wait until the slot table is empty.
		 * Schedule the reset thread
		 */
558
		rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
559
		spin_unlock(&tbl->slot_tbl_lock);
Trond Myklebust's avatar
Trond Myklebust committed
560
		dprintk("%s Schedule Session Reset\n", __func__);
561
		return -EAGAIN;
562 563
	}

564 565 566 567 568 569 570 571
	if (!rpc_queue_empty(&tbl->slot_tbl_waitq) &&
	    !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) {
		rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
		spin_unlock(&tbl->slot_tbl_lock);
		dprintk("%s enforce FIFO order\n", __func__);
		return -EAGAIN;
	}

572
	slotid = nfs4_find_slot(tbl);
Andy Adamson's avatar
Andy Adamson committed
573 574 575 576 577 578 579 580
	if (slotid == NFS4_MAX_SLOT_TABLE) {
		rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
		spin_unlock(&tbl->slot_tbl_lock);
		dprintk("<-- %s: no free slots\n", __func__);
		return -EAGAIN;
	}
	spin_unlock(&tbl->slot_tbl_lock);

581
	rpc_task_set_priority(task, RPC_PRIORITY_NORMAL);
Andy Adamson's avatar
Andy Adamson committed
582
	slot = tbl->slots + slotid;
Benny Halevy's avatar
Benny Halevy committed
583
	args->sa_session = session;
Andy Adamson's avatar
Andy Adamson committed
584 585 586 587 588
	args->sa_slotid = slotid;
	args->sa_cache_this = cache_reply;

	dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr);

Benny Halevy's avatar
Benny Halevy committed
589
	res->sr_session = session;
590
	res->sr_slot = slot;
Andy Adamson's avatar
Andy Adamson committed
591
	res->sr_renewal_time = jiffies;
592
	res->sr_status_flags = 0;
Andy Adamson's avatar
Andy Adamson committed
593 594 595 596 597
	/*
	 * sr_status is only set in decode_sequence, and so will remain
	 * set to 1 if an rpc level failure occurs.
	 */
	res->sr_status = 1;
Andy Adamson's avatar
Andy Adamson committed
598 599
	return 0;
}
Andy Adamson's avatar
Andy Adamson committed
600
EXPORT_SYMBOL_GPL(nfs41_setup_sequence);
Andy Adamson's avatar
Andy Adamson committed
601

602
int nfs4_setup_sequence(const struct nfs_server *server,
Andy Adamson's avatar
Andy Adamson committed
603 604 605 606 607
			struct nfs4_sequence_args *args,
			struct nfs4_sequence_res *res,
			int cache_reply,
			struct rpc_task *task)
{
608
	struct nfs4_session *session = nfs4_get_session(server);
Andy Adamson's avatar
Andy Adamson committed
609 610
	int ret = 0;

611 612 613 614 615 616
	if (session == NULL) {
		args->sa_session = NULL;
		res->sr_session = NULL;
		goto out;
	}

617
	dprintk("--> %s clp %p session %p sr_slot %td\n",
618 619
		__func__, session->clp, session, res->sr_slot ?
			res->sr_slot - session->fc_slot_table.slots : -1);
Andy Adamson's avatar
Andy Adamson committed
620

621
	ret = nfs41_setup_sequence(session, args, res, cache_reply,
Andy Adamson's avatar
Andy Adamson committed
622 623 624 625 626 627 628
				   task);
out:
	dprintk("<-- %s status=%d\n", __func__, ret);
	return ret;
}

struct nfs41_call_sync_data {
629
	const struct nfs_server *seq_server;
Andy Adamson's avatar
Andy Adamson committed
630 631 632 633 634 635 636 637 638
	struct nfs4_sequence_args *seq_args;
	struct nfs4_sequence_res *seq_res;
	int cache_reply;
};

static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs41_call_sync_data *data = calldata;

639 640 641
	dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server);

	if (nfs4_setup_sequence(data->seq_server, data->seq_args,
Andy Adamson's avatar
Andy Adamson committed
642 643 644 645 646
				data->seq_res, data->cache_reply, task))
		return;
	rpc_call_start(task);
}

647 648 649 650 651 652
static void nfs41_call_priv_sync_prepare(struct rpc_task *task, void *calldata)
{
	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
	nfs41_call_sync_prepare(task, calldata);
}

Andy Adamson's avatar
Andy Adamson committed
653 654 655 656
static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
{
	struct nfs41_call_sync_data *data = calldata;

657
	nfs41_sequence_done(task, data->seq_res);
Andy Adamson's avatar
Andy Adamson committed
658 659
}

Andy Adamson's avatar
Andy Adamson committed
660 661
struct rpc_call_ops nfs41_call_sync_ops = {
	.rpc_call_prepare = nfs41_call_sync_prepare,
Andy Adamson's avatar
Andy Adamson committed
662
	.rpc_call_done = nfs41_call_sync_done,
Andy Adamson's avatar
Andy Adamson committed
663 664
};

665 666 667 668 669
struct rpc_call_ops nfs41_call_priv_sync_ops = {
	.rpc_call_prepare = nfs41_call_priv_sync_prepare,
	.rpc_call_done = nfs41_call_sync_done,
};

670 671
static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
				   struct nfs_server *server,
Andy Adamson's avatar
Andy Adamson committed
672 673 674
				   struct rpc_message *msg,
				   struct nfs4_sequence_args *args,
				   struct nfs4_sequence_res *res,
675 676
				   int cache_reply,
				   int privileged)
Andy Adamson's avatar
Andy Adamson committed
677 678 679 680
{
	int ret;
	struct rpc_task *task;
	struct nfs41_call_sync_data data = {
681
		.seq_server = server,
Andy Adamson's avatar
Andy Adamson committed
682 683 684 685 686
		.seq_args = args,
		.seq_res = res,
		.cache_reply = cache_reply,
	};
	struct rpc_task_setup task_setup = {
687
		.rpc_client = clnt,
Andy Adamson's avatar
Andy Adamson committed
688 689 690 691 692
		.rpc_message = msg,
		.callback_ops = &nfs41_call_sync_ops,
		.callback_data = &data
	};

693
	res->sr_slot = NULL;
694 695
	if (privileged)
		task_setup.callback_ops = &nfs41_call_priv_sync_ops;
Andy Adamson's avatar
Andy Adamson committed
696 697 698 699 700 701 702 703 704 705
	task = rpc_run_task(&task_setup);
	if (IS_ERR(task))
		ret = PTR_ERR(task);
	else {
		ret = task->tk_status;
		rpc_put_task(task);
	}
	return ret;
}

706 707
int _nfs4_call_sync_session(struct rpc_clnt *clnt,
			    struct nfs_server *server,
Andy Adamson's avatar
Andy Adamson committed
708 709 710 711 712
			    struct rpc_message *msg,
			    struct nfs4_sequence_args *args,
			    struct nfs4_sequence_res *res,
			    int cache_reply)
{
713
	return nfs4_call_sync_sequence(clnt, server, msg, args, res, cache_reply, 0);
Andy Adamson's avatar
Andy Adamson committed
714 715
}

716
#else
717 718
static int nfs4_sequence_done(struct rpc_task *task,
			       struct nfs4_sequence_res *res)
719
{
720
	return 1;
721
}
Andy Adamson's avatar
Andy Adamson committed
722 723
#endif /* CONFIG_NFS_V4_1 */

724 725
int _nfs4_call_sync(struct rpc_clnt *clnt,
		    struct nfs_server *server,
Andy Adamson's avatar
Andy Adamson committed
726 727 728 729 730
		    struct rpc_message *msg,
		    struct nfs4_sequence_args *args,
		    struct nfs4_sequence_res *res,
		    int cache_reply)
{
731
	args->sa_session = res->sr_session = NULL;
732
	return rpc_call_sync(clnt, msg, 0);
Andy Adamson's avatar
Andy Adamson committed
733 734
}

735
static inline
736 737
int nfs4_call_sync(struct rpc_clnt *clnt,
		   struct nfs_server *server,
738 739 740 741 742
		   struct rpc_message *msg,
		   struct nfs4_sequence_args *args,
		   struct nfs4_sequence_res *res,
		   int cache_reply)
{
743 744
	return server->nfs_client->cl_mvops->call_sync(clnt, server, msg,
						args, res, cache_reply);
745
}
Andy Adamson's avatar
Andy Adamson committed
746

747
static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
Linus Torvalds's avatar
Linus Torvalds committed
748
{
749
	struct nfs_inode *nfsi = NFS_I(dir);
Linus Torvalds's avatar
Linus Torvalds committed
750

751
	spin_lock(&dir->i_lock);
752
	nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
753
	if (!cinfo->atomic || cinfo->before != dir->i_version)
754
		nfs_force_lookup_revalidate(dir);
755
	dir->i_version = cinfo->after;
756
	spin_unlock(&dir->i_lock);
Linus Torvalds's avatar
Linus Torvalds committed
757 758
}

759
struct nfs4_opendata {
760
	struct kref kref;
761 762
	struct nfs_openargs o_arg;
	struct nfs_openres o_res;
763 764
	struct nfs_open_confirmargs c_arg;
	struct nfs_open_confirmres c_res;
765 766 767
	struct nfs_fattr f_attr;
	struct nfs_fattr dir_attr;
	struct dentry *dir;
768
	struct dentry *dentry;
769
	struct nfs4_state_owner *owner;
770
	struct nfs4_state *state;
771
	struct iattr attrs;
772
	unsigned long timestamp;
773
	unsigned int rpc_done : 1;
774 775
	int rpc_status;
	int cancelled;
776 777
};

778 779 780 781 782

static void nfs4_init_opendata_res(struct nfs4_opendata *p)
{
	p->o_res.f_attr = &p->f_attr;
	p->o_res.dir_attr = &p->dir_attr;
783 784
	p->o_res.seqid = p->o_arg.seqid;
	p->c_res.seqid = p->c_arg.seqid;
785 786 787 788 789
	p->o_res.server = p->o_arg.server;
	nfs_fattr_init(&p->f_attr);
	nfs_fattr_init(&p->dir_attr);
}

790
static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
791
		struct nfs4_state_owner *sp, fmode_t fmode, int flags,
792 793
		const struct iattr *attrs,
		gfp_t gfp_mask)
794
{
795
	struct dentry *parent = dget_parent(dentry);
796 797 798 799
	struct inode *dir = parent->d_inode;
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs4_opendata *p;

800
	p = kzalloc(sizeof(*p), gfp_mask);
801 802
	if (p == NULL)
		goto err;
803
	p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
804 805
	if (p->o_arg.seqid == NULL)
		goto err_free;
806 807
	nfs_sb_active(dentry->d_sb);
	p->dentry = dget(dentry);
808 809 810 811
	p->dir = parent;
	p->owner = sp;
	atomic_inc(&sp->so_count);
	p->o_arg.fh = NFS_FH(dir);
812 813
	p->o_arg.open_flags = flags;
	p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
814
	p->o_arg.clientid = server->nfs_client->cl_clientid;
815
	p->o_arg.id = sp->so_owner_id.id;
816
	p->o_arg.name = &dentry->d_name;
817 818 819
	p->o_arg.server = server;
	p->o_arg.bitmask = server->attr_bitmask;
	p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
820 821 822
	if (flags & O_CREAT) {
		u32 *s;

823 824
		p->o_arg.u.attrs = &p->attrs;
		memcpy(&p->attrs, attrs, sizeof(p->attrs));
825 826 827
		s = (u32 *) p->o_arg.u.verifier.data;
		s[0] = jiffies;
		s[1] = current->pid;
828
	}
829 830 831
	p->c_arg.fh = &p->o_res.fh;
	p->c_arg.stateid = &p->o_res.stateid;
	p->c_arg.seqid = p->o_arg.seqid;
832
	nfs4_init_opendata_res(p);
833
	kref_init(&p->kref);
834 835 836 837 838 839 840 841
	return p;
err_free:
	kfree(p);
err:
	dput(parent);
	return NULL;
}

842
static void nfs4_opendata_free(struct kref *kref)
843
{
844 845
	struct nfs4_opendata *p = container_of(kref,
			struct nfs4_opendata, kref);
846
	struct super_block *sb = p->dentry->d_sb;
847 848

	nfs_free_seqid(p->o_arg.seqid);
849 850
	if (p->state != NULL)
		nfs4_put_open_state(p->state);
851 852
	nfs4_put_state_owner(p->owner);
	dput(p->dir);
853 854
	dput(p->dentry);
	nfs_sb_deactive(sb);
855 856 857 858 859 860 861
	kfree(p);
}

static void nfs4_opendata_put(struct nfs4_opendata *p)
{
	if (p != NULL)
		kref_put(&p->kref, nfs4_opendata_free);
862 863
}

864 865 866 867 868 869 870 871
static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
{
	int ret;

	ret = rpc_wait_for_completion_task(task);
	return ret;
}

872
static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
873 874
{
	int ret = 0;
875 876 877 878

	if (open_mode & O_EXCL)
		goto out;
	switch (mode & (FMODE_READ|FMODE_WRITE)) {
879
		case FMODE_READ:
880 881
			ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0
				&& state->n_rdonly != 0;
882 883
			break;
		case FMODE_WRITE:
884 885
			ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0
				&& state->n_wronly != 0;
886 887
			break;
		case FMODE_READ|FMODE_WRITE:
888 889
			ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0
				&& state->n_rdwr != 0;
890
	}
891
out:
892 893 894
	return ret;
}

895
static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
896
{
897
	if ((delegation->type & fmode) != fmode)
898
		return 0;
899
	if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
900
		return 0;
901
	nfs_mark_delegation_referenced(delegation);
902 903 904
	return 1;
}

905
static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
906
{
907
	switch (fmode) {
908 909 910 911 912 913 914 915 916
		case FMODE_WRITE:
			state->n_wronly++;
			break;
		case FMODE_READ:
			state->n_rdonly++;
			break;
		case FMODE_READ|FMODE_WRITE:
			state->n_rdwr++;
	}
917
	nfs4_state_set_mode_locked(state, state->state | fmode);
918 919
}

920
static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
921 922 923 924
{
	if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
		memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data));
	memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data));
925
	switch (fmode) {
926 927 928 929 930 931 932 933 934
		case FMODE_READ:
			set_bit(NFS_O_RDONLY_STATE, &state->flags);
			break;
		case FMODE_WRITE:
			set_bit(NFS_O_WRONLY_STATE, &state->flags);
			break;
		case FMODE_READ|FMODE_WRITE:
			set_bit(NFS_O_RDWR_STATE, &state->flags);
	}
935 936
}

937
static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
938
{
939
	write_seqlock(&state->seqlock);
940
	nfs_set_open_stateid_locked(state, stateid, fmode);
941
	write_sequnlock(&state->seqlock);
942 943
}

944
static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
Linus Torvalds's avatar
Linus Torvalds committed
945
{
946 947 948 949 950
	/*
	 * Protect the call to nfs4_state_set_mode_locked and
	 * serialise the stateid update
	 */
	write_seqlock(&state->seqlock);
951 952 953 954 955
	if (deleg_stateid != NULL) {
		memcpy(state->stateid.data, deleg_stateid->data, sizeof(state->stateid.data));
		set_bit(NFS_DELEGATED_STATE, &state->flags);
	}
	if (open_stateid != NULL)
956
		nfs_set_open_stateid_locked(state, open_stateid, fmode);
957 958
	write_sequnlock(&state->seqlock);
	spin_lock(&state->owner->so_lock);
959
	update_open_stateflags(state, fmode);
960
	spin_unlock(&state->owner->so_lock);
Linus Torvalds's avatar
Linus Torvalds committed
961 962
}

963
static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
964 965 966 967 968
{
	struct nfs_inode *nfsi = NFS_I(state->inode);
	struct nfs_delegation *deleg_cur;
	int ret = 0;

969
	fmode &= (FMODE_READ|FMODE_WRITE);
970 971 972 973 974 975 976 977

	rcu_read_lock();
	deleg_cur = rcu_dereference(nfsi->delegation);
	if (deleg_cur == NULL)
		goto no_delegation;

	spin_lock(&deleg_cur->lock);
	if (nfsi->delegation != deleg_cur ||
978
	    (deleg_cur->type & fmode) != fmode)
979 980 981 982 983 984 985
		goto no_delegation_unlock;

	if (delegation == NULL)
		delegation = &deleg_cur->stateid;
	else if (memcmp(deleg_cur->stateid.data, delegation->data, NFS4_STATEID_SIZE) != 0)
		goto no_delegation_unlock;

986
	nfs_mark_delegation_referenced(deleg_cur);
987
	__update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
988 989 990 991 992 993 994
	ret = 1;
no_delegation_unlock:
	spin_unlock(&deleg_cur->lock);
no_delegation:
	rcu_read_unlock();

	if (!ret && open_stateid != NULL) {
995
		__update_open_stateid(state, open_stateid, NULL, fmode);
996 997 998 999 1000 1001 1002
		ret = 1;
	}

	return ret;
}


1003
static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
1004 1005 1006 1007 1008
{
	struct nfs_delegation *delegation;

	rcu_read_lock();
	delegation = rcu_dereference(NFS_I(inode)->delegation);
1009
	if (delegation == NULL || (delegation->type & fmode) == fmode) {
1010 1011 1012 1013 1014 1015 1016
		rcu_read_unlock();
		return;
	}
	rcu_read_unlock();
	nfs_inode_return_delegation(inode);
}

1017
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
1018 1019 1020 1021
{
	struct nfs4_state *state = opendata->state;
	struct nfs_inode *nfsi = NFS_I(state->inode);
	struct nfs_delegation *delegation;
1022 1023
	int open_mode = opendata->o_arg.open_flags & O_EXCL;
	fmode_t fmode = opendata->o_arg.fmode;
1024 1025 1026 1027
	nfs4_stateid stateid;
	int ret = -EAGAIN;

	for (;;) {
1028
		if (can_open_cached(state, fmode, open_mode)) {
1029
			spin_lock(&state->owner->so_lock);
1030 1031
			if (can_open_cached(state, fmode, open_mode)) {
				update_open_stateflags(state, fmode);
1032 1033 1034 1035 1036
				spin_unlock(&state->owner->so_lock);
				goto out_return_state;
			}
			spin_unlock(&state->owner->so_lock);
		}
1037 1038 1039
		rcu_read_lock();
		delegation = rcu_dereference(nfsi->delegation);
		if (delegation == NULL ||
1040
		    !can_open_delegated(delegation, fmode)) {
1041
			rcu_read_unlock();
1042
			break;
1043
		}
1044 1045 1046
		/* Save the delegation */
		memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
		rcu_read_unlock();
1047
		ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
1048 1049 1050
		if (ret != 0)
			goto out;
		ret = -EAGAIN;
1051 1052

		/* Try to update the stateid using the delegation */
1053
		if (update_open_stateid(state, NULL, &stateid, fmode))
1054
			goto out_return_state;
1055 1056 1057 1058 1059 1060 1061 1062
	}
out:
	return ERR_PTR(ret);
out_return_state:
	atomic_inc(&state->count);
	return state;
}

1063 1064 1065 1066
static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
{
	struct inode *inode;
	struct nfs4_state *state = NULL;
1067
	struct nfs_delegation *delegation;
1068
	int ret;
1069

1070
	if (!data->rpc_done) {
1071
		state = nfs4_try_open_cached(data);
1072 1073 1074
		goto out;
	}

1075
	ret = -EAGAIN;
1076
	if (!(data->f_attr.valid & NFS_ATTR_FATTR))
1077
		goto err;
1078
	inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
1079
	ret = PTR_ERR(inode);
1080
	if (IS_ERR(inode))
1081 1082
		goto err;
	ret = -ENOMEM;
1083 1084
	state = nfs4_get_open_state(inode, data->owner);
	if (state == NULL)
1085
		goto err_put_inode;
1086 1087 1088
	if (data->o_res.delegation_type != 0) {
		int delegation_flags = 0;

1089 1090 1091 1092 1093
		rcu_read_lock();
		delegation = rcu_dereference(NFS_I(inode)->delegation);
		if (delegation)
			delegation_flags = delegation->flags;
		rcu_read_unlock();
1094
		if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
1095 1096 1097 1098 1099 1100 1101 1102
			nfs_inode_set_delegation(state->inode,
					data->owner->so_cred,
					&data->o_res);
		else
			nfs_inode_reclaim_delegation(state->inode,
					data->owner->so_cred,
					&data->o_res);
	}
1103 1104

	update_open_stateid(state, &data->o_res.stateid, NULL,
1105
			data->o_arg.fmode);
1106
	iput(inode);
1107
out:
1108
	return state;
1109 1110 1111 1112
err_put_inode:
	iput(inode);
err:
	return ERR_PTR(ret);
1113 1114
}

1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state)
{
	struct nfs_inode *nfsi = NFS_I(state->inode);
	struct nfs_open_context *ctx;

	spin_lock(&state->inode->i_lock);
	list_for_each_entry(ctx, &nfsi->open_files, list) {
		if (ctx->state != state)
			continue;
		get_nfs_open_context(ctx);
		spin_unlock(&state->inode->i_lock);
		return ctx;
	}
	spin_unlock(&state->inode->i_lock);
	return ERR_PTR(-ENOENT);
}

1132 1133 1134 1135
static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx, struct nfs4_state *state)
{
	struct nfs4_opendata *opendata;

1136
	opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, NULL, GFP_NOFS);
1137 1138 1139 1140 1141 1142 1143
	if (opendata == NULL)
		return ERR_PTR(-ENOMEM);
	opendata->state = state;
	atomic_inc(&state->count);
	return opendata;
}

1144
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
1145
{
1146
	struct nfs4_state *newstate;
1147 1148
	int ret;

1149 1150
	opendata->o_arg.open_flags = 0;
	opendata->o_arg.fmode = fmode;
1151 1152 1153
	memset(&opendata->o_res, 0, sizeof(opendata->o_res));
	memset(&opendata->c_res, 0, sizeof(opendata->c_res));
	nfs4_init_opendata_res(opendata);
1154
	ret = _nfs4_recover_proc_open(opendata);
1155 1156
	if (ret != 0)
		return ret; 
1157
	newstate = nfs4_opendata_to_nfs4_state(opendata);
1158 1159
	if (IS_ERR(newstate))
		return PTR_ERR(newstate);
1160
	nfs4_close_state(newstate, fmode);
1161
	*res = newstate;
1162 1163 1164 1165 1166 1167 1168 1169 1170
	return 0;
}

static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
{
	struct nfs4_state *newstate;
	int ret;

	/* memory barrier prior to reading state->n_* */
1171
	clear_bit(NFS_DELEGATED_STATE, &state->flags);
1172 1173
	smp_rmb();
	if (state->n_rdwr != 0) {
Trond Myklebust's avatar
Trond Myklebust committed
1174
		clear_bit(NFS_O_RDWR_STATE, &state->flags);
1175
		ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
1176 1177
		if (ret != 0)
			return ret;
1178 1179
		if (newstate != state)
			return -ESTALE;
1180 1181
	}
	if (state->n_wronly != 0) {
Trond Myklebust's avatar
Trond Myklebust committed
1182
		clear_bit(NFS_O_WRONLY_STATE, &state->flags);
1183
		ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
1184 1185
		if (ret != 0)
			return ret;
1186 1187
		if (newstate != state)
			return -ESTALE;
1188 1189
	}
	if (state->n_rdonly != 0) {
Trond Myklebust's avatar
Trond Myklebust committed
1190
		clear_bit(NFS_O_RDONLY_STATE, &state->flags);
1191
		ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
1192 1193
		if (ret != 0)
			return ret;
1194 1195
		if (newstate != state)
			return -ESTALE;
1196
	}
1197 1198 1199 1200 1201 1202
	/*
	 * We may have performed cached opens for all three recoveries.
	 * Check if we need to update the current stateid.
	 */
	if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0 &&
	    memcmp(state->stateid.data, state->open_stateid.data, sizeof(state->stateid.data)) != 0) {
1203
		write_seqlock(&state->seqlock);
1204 1205
		if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
			memcpy(state->stateid.data, state->open_stateid.data, sizeof(state->stateid.data));
1206
		write_sequnlock(&state->seqlock);
1207
	}
1208 1209 1210
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
1211 1212 1213 1214
/*
 * OPEN_RECLAIM:
 * 	reclaim state on the server after a reboot.
 */
1215
static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
Linus Torvalds's avatar
Linus Torvalds committed
1216
{
1217
	struct nfs_delegation *delegation;
1218
	struct nfs4_opendata *opendata;
1219
	fmode_t delegation_type = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1220 1221
	int status;

1222 1223 1224
	opendata = nfs4_open_recoverdata_alloc(ctx, state);
	if (IS_ERR(opendata))
		return PTR_ERR(opendata);
1225 1226
	opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS;
	opendata->o_arg.fh = NFS_FH(state->inode);
1227 1228
	rcu_read_lock();
	delegation = rcu_dereference(NFS_I(state->inode)->delegation);
1229
	if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
1230
		delegation_type = delegation->type;
1231
	rcu_read_unlock();
1232 1233
	opendata->o_arg.u.delegation_type = delegation_type;
	status = nfs4_open_recover(opendata, state);
1234
	nfs4_opendata_put(opendata);
Linus Torvalds's avatar
Linus Torvalds committed
1235 1236 1237
	return status;
}

1238
static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
Linus Torvalds's avatar
Linus Torvalds committed
1239 1240 1241 1242 1243
{
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs4_exception exception = { };
	int err;
	do {
1244
		err = _nfs4_do_open_reclaim(ctx, state);
1245
		if (err != -NFS4ERR_DELAY)
1246 1247
			break;
		nfs4_handle_exception(server, err, &exception);
Linus Torvalds's avatar
Linus Torvalds committed
1248 1249 1250 1251
	} while (exception.retry);
	return err;
}

1252 1253 1254 1255 1256 1257 1258 1259
static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
	struct nfs_open_context *ctx;
	int ret;

	ctx = nfs4_state_find_open_context(state);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);
1260
	ret = nfs4_do_open_reclaim(ctx, state);
1261 1262 1263 1264
	put_nfs_open_context(ctx);
	return ret;
}

1265
static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
Linus Torvalds's avatar
Linus Torvalds committed
1266
{
1267
	struct nfs4_opendata *opendata;
1268
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
1269

1270 1271 1272
	opendata = nfs4_open_recoverdata_alloc(ctx, state);
	if (IS_ERR(opendata))
		return PTR_ERR(opendata);
1273
	opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR;
1274
	memcpy(opendata->o_arg.u.delegation.data, stateid->data,
1275
			sizeof(opendata->o_arg.u.delegation.data));
1276
	ret = nfs4_open_recover(opendata, state);
1277
	nfs4_opendata_put(opendata);
1278
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
1279 1280
}

1281
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
Linus Torvalds's avatar
Linus Torvalds committed
1282 1283
{
	struct nfs4_exception exception = { };
1284
	struct nfs_server *server = NFS_SERVER(state->inode);
Linus Torvalds's avatar
Linus Torvalds committed
1285 1286
	int err;
	do {
1287
		err = _nfs4_open_delegation_recall(ctx, state, stateid);
Linus Torvalds's avatar
Linus Torvalds committed
1288 1289
		switch (err) {
			case 0:
1290 1291 1292
			case -ENOENT:
			case -ESTALE:
				goto out;
1293 1294 1295 1296 1297
			case -NFS4ERR_BADSESSION:
			case -NFS4ERR_BADSLOT:
			case -NFS4ERR_BAD_HIGH_SLOT:
			case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
			case -NFS4ERR_DEADSESSION:
1298
				nfs4_schedule_session_recovery(server->nfs_client->cl_session);
1299
				goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1300 1301 1302 1303
			case -NFS4ERR_STALE_CLIENTID:
			case -NFS4ERR_STALE_STATEID:
			case -NFS4ERR_EXPIRED:
				/* Don't recall a delegation if it was lost */
1304
				nfs4_schedule_lease_recovery(server->nfs_client);
1305 1306 1307 1308 1309 1310 1311 1312
				goto out;
			case -ERESTARTSYS:
				/*
				 * The show must go on: exit, but mark the
				 * stateid as needing recovery.
				 */
			case -NFS4ERR_ADMIN_REVOKED:
			case -NFS4ERR_BAD_STATEID:
1313
				nfs4_schedule_stateid_recovery(server, state);
1314 1315 1316 1317 1318 1319 1320
			case -EKEYEXPIRED:
				/*
				 * User RPCSEC_GSS context has expired.
				 * We cannot recover this stateid now, so
				 * skip it and allow recovery thread to
				 * proceed.
				 */
1321 1322 1323
			case -ENOMEM:
				err = 0;
				goto out;
Linus Torvalds's avatar
Linus Torvalds committed
1324 1325 1326
		}
		err = nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
1327
out:
Linus Torvalds's avatar
Linus Torvalds committed
1328 1329 1330
	return err;
}

1331 1332 1333 1334 1335
static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_opendata *data = calldata;

	data->rpc_status = task->tk_status;
1336
	if (data->rpc_status == 0) {
1337 1338
		memcpy(data->o_res.stateid.data, data->c_res.stateid.data,
				sizeof(data->o_res.stateid.data));
1339
		nfs_confirm_seqid(&data->owner->so_seqid, 0);
1340
		renew_lease(data->o_res.server, data->timestamp);
1341
		data->rpc_done = 1;
1342
	}
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
}

static void nfs4_open_confirm_release(void *calldata)
{
	struct nfs4_opendata *data = calldata;
	struct nfs4_state *state = NULL;

	/* If this request hasn't been cancelled, do nothing */
	if (data->cancelled == 0)
		goto out_free;
	/* In case of error, no cleanup! */
1354
	if (!data->rpc_done)
1355 1356
		goto out_free;
	state = nfs4_opendata_to_nfs4_state(data);
1357
	if (!IS_ERR(state))
1358
		nfs4_close_state(state, data->o_arg.fmode);
1359
out_free:
1360
	nfs4_opendata_put(data);
1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
}

static const struct rpc_call_ops nfs4_open_confirm_ops = {
	.rpc_call_done = nfs4_open_confirm_done,
	.rpc_release = nfs4_open_confirm_release,
};

/*
 * Note: On error, nfs4_proc_open_confirm will free the struct nfs4_opendata
 */
static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
{
	struct nfs_server *server = NFS_SERVER(data->dir->d_inode);
	struct rpc_task *task;
1375 1376 1377 1378 1379 1380
	struct  rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
		.rpc_argp = &data->c_arg,
		.rpc_resp = &data->c_res,
		.rpc_cred = data->owner->so_cred,
	};
1381 1382
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
1383
		.rpc_message = &msg,
1384 1385
		.callback_ops = &nfs4_open_confirm_ops,
		.callback_data = data,
1386
		.workqueue = nfsiod_workqueue,
1387 1388
		.flags = RPC_TASK_ASYNC,
	};
Linus Torvalds's avatar
Linus Torvalds committed
1389 1390
	int status;

1391
	kref_get(&data->kref);
1392 1393
	data->rpc_done = 0;
	data->rpc_status = 0;
1394
	data->timestamp = jiffies;
1395
	task = rpc_run_task(&task_setup_data);
1396
	if (IS_ERR(task))
1397 1398 1399 1400 1401 1402 1403
		return PTR_ERR(task);
	status = nfs4_wait_for_completion_rpc_task(task);
	if (status != 0) {
		data->cancelled = 1;
		smp_wmb();
	} else
		status = data->rpc_status;
1404
	rpc_put_task(task);
Linus Torvalds's avatar
Linus Torvalds committed
1405 1406 1407
	return status;
}

1408
static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
Linus Torvalds's avatar
Linus Torvalds committed
1409
{
1410 1411
	struct nfs4_opendata *data = calldata;
	struct nfs4_state_owner *sp = data->owner;
1412

1413 1414
	if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
		return;
1415 1416 1417 1418 1419 1420 1421
	/*
	 * Check if we still need to send an OPEN call, or if we can use
	 * a delegation instead.
	 */
	if (data->state != NULL) {
		struct nfs_delegation *delegation;

1422
		if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
1423
			goto out_no_action;
1424 1425 1426
		rcu_read_lock();
		delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
		if (delegation != NULL &&
1427
		    test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) {
1428
			rcu_read_unlock();
1429
			goto out_no_action;
1430 1431 1432
		}
		rcu_read_unlock();
	}
1433
	/* Update sequence id. */
1434
	data->o_arg.id = sp->so_owner_id.id;
1435
	data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid;
1436
	if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) {
1437
		task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
1438 1439
		nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
	}
1440
	data->timestamp = jiffies;
1441
	if (nfs4_setup_sequence(data->o_arg.server,
1442 1443 1444
				&data->o_arg.seq_args,
				&data->o_res.seq_res, 1, task))
		return;
1445
	rpc_call_start(task);
1446 1447 1448 1449
	return;
out_no_action:
	task->tk_action = NULL;

1450
}
Linus Torvalds's avatar
Linus Torvalds committed
1451

1452 1453 1454 1455 1456 1457
static void nfs4_recover_open_prepare(struct rpc_task *task, void *calldata)
{
	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
	nfs4_open_prepare(task, calldata);
}

1458 1459 1460
static void nfs4_open_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_opendata *data = calldata;
Linus Torvalds's avatar
Linus Torvalds committed
1461

1462
	data->rpc_status = task->tk_status;
1463

1464 1465
	if (!nfs4_sequence_done(task, &data->o_res.seq_res))
		return;
1466

1467 1468
	if (task->tk_status == 0) {
		switch (data->o_res.f_attr->mode & S_IFMT) {
1469 1470 1471
			case S_IFREG:
				break;
			case S_IFLNK:
1472
				data->rpc_status = -ELOOP;
1473 1474
				break;
			case S_IFDIR:
1475
				data->rpc_status = -EISDIR;
1476 1477
				break;
			default:
1478
				data->rpc_status = -ENOTDIR;
1479
		}
1480
		renew_lease(data->o_res.server, data->timestamp);
1481 1482
		if (!(data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM))
			nfs_confirm_seqid(&data->owner->so_seqid, 0);
1483
	}
1484
	data->rpc_done = 1;
1485
}
1486

1487 1488 1489 1490 1491 1492 1493 1494 1495
static void nfs4_open_release(void *calldata)
{
	struct nfs4_opendata *data = calldata;
	struct nfs4_state *state = NULL;

	/* If this request hasn't been cancelled, do nothing */
	if (data->cancelled == 0)
		goto out_free;
	/* In case of error, no cleanup! */
1496
	if (data->rpc_status != 0 || !data->rpc_done)
1497 1498 1499 1500 1501
		goto out_free;
	/* In case we need an open_confirm, no cleanup! */
	if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM)
		goto out_free;
	state = nfs4_opendata_to_nfs4_state(data);
1502
	if (!IS_ERR(state))
1503
		nfs4_close_state(state, data->o_arg.fmode);
1504
out_free:
1505
	nfs4_opendata_put(data);
1506 1507 1508 1509 1510 1511 1512 1513
}

static const struct rpc_call_ops nfs4_open_ops = {
	.rpc_call_prepare = nfs4_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

1514 1515 1516 1517 1518 1519 1520
static const struct rpc_call_ops nfs4_recover_open_ops = {
	.rpc_call_prepare = nfs4_recover_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
1521 1522 1523 1524 1525 1526
{
	struct inode *dir = data->dir->d_inode;
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs_openargs *o_arg = &data->o_arg;
	struct nfs_openres *o_res = &data->o_res;
	struct rpc_task *task;
1527 1528 1529 1530 1531 1532
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN],
		.rpc_argp = o_arg,
		.rpc_resp = o_res,
		.rpc_cred = data->owner->so_cred,
	};
1533 1534
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
1535
		.rpc_message = &msg,
1536 1537
		.callback_ops = &nfs4_open_ops,
		.callback_data = data,
1538
		.workqueue = nfsiod_workqueue,
1539 1540
		.flags = RPC_TASK_ASYNC,
	};
1541 1542
	int status;

1543
	kref_get(&data->kref);
1544 1545
	data->rpc_done = 0;
	data->rpc_status = 0;
1546
	data->cancelled = 0;
1547 1548
	if (isrecover)
		task_setup_data.callback_ops = &nfs4_recover_open_ops;
1549
	task = rpc_run_task(&task_setup_data);
1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595
        if (IS_ERR(task))
                return PTR_ERR(task);
        status = nfs4_wait_for_completion_rpc_task(task);
        if (status != 0) {
                data->cancelled = 1;
                smp_wmb();
        } else
                status = data->rpc_status;
        rpc_put_task(task);

	return status;
}

static int _nfs4_recover_proc_open(struct nfs4_opendata *data)
{
	struct inode *dir = data->dir->d_inode;
	struct nfs_openres *o_res = &data->o_res;
        int status;

	status = nfs4_run_open_task(data, 1);
	if (status != 0 || !data->rpc_done)
		return status;

	nfs_refresh_inode(dir, o_res->dir_attr);

	if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
		status = _nfs4_proc_open_confirm(data);
		if (status != 0)
			return status;
	}

	return status;
}

/*
 * Note: On error, nfs4_proc_open will free the struct nfs4_opendata
 */
static int _nfs4_proc_open(struct nfs4_opendata *data)
{
	struct inode *dir = data->dir->d_inode;
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs_openargs *o_arg = &data->o_arg;
	struct nfs_openres *o_res = &data->o_res;
	int status;

	status = nfs4_run_open_task(data, 0);
1596 1597 1598 1599 1600 1601
	if (!data->rpc_done)
		return status;
	if (status != 0) {
		if (status == -NFS4ERR_BADNAME &&
				!(o_arg->open_flags & O_CREAT))
			return -ENOENT;
1602
		return status;
1603
	}
1604

1605 1606 1607 1608 1609
	if (o_arg->open_flags & O_CREAT) {
		update_changeattr(dir, &o_res->cinfo);
		nfs_post_op_update_inode(dir, o_res->dir_attr);
	} else
		nfs_refresh_inode(dir, o_res->dir_attr);
Trond Myklebust's avatar
Trond Myklebust committed
1610 1611
	if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
		server->caps &= ~NFS_CAP_POSIX_LOCK;
Linus Torvalds's avatar
Linus Torvalds committed
1612
	if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
1613
		status = _nfs4_proc_open_confirm(data);
Linus Torvalds's avatar
Linus Torvalds committed
1614
		if (status != 0)
1615
			return status;
Linus Torvalds's avatar
Linus Torvalds committed
1616 1617
	}
	if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
1618
		_nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr);
1619
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1620 1621
}

Andy Adamson's avatar
Andy Adamson committed
1622
static int nfs4_client_recover_expired_lease(struct nfs_client *clp)
1623
{
1624
	unsigned int loop;
1625
	int ret;
1626

1627
	for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
1628
		ret = nfs4_wait_clnt_recover(clp);
1629
		if (ret != 0)
1630
			break;
1631 1632
		if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
		    !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
1633
			break;
1634
		nfs4_schedule_state_manager(clp);
1635
		ret = -EIO;
1636
	}
1637
	return ret;
1638 1639
}

Andy Adamson's avatar
Andy Adamson committed
1640 1641 1642 1643 1644
static int nfs4_recover_expired_lease(struct nfs_server *server)
{
	return nfs4_client_recover_expired_lease(server->nfs_client);
}

Linus Torvalds's avatar
Linus Torvalds committed
1645 1646 1647 1648 1649
/*
 * OPEN_EXPIRED:
 * 	reclaim state on the server after a network partition.
 * 	Assumes caller holds the appropriate lock
 */
1650
static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state)
Linus Torvalds's avatar
Linus Torvalds committed
1651
{
1652
	struct nfs4_opendata *opendata;
1653
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
1654

1655 1656 1657
	opendata = nfs4_open_recoverdata_alloc(ctx, state);
	if (IS_ERR(opendata))
		return PTR_ERR(opendata);
1658
	ret = nfs4_open_recover(opendata, state);
1659
	if (ret == -ESTALE)
1660
		d_drop(ctx->dentry);
1661
	nfs4_opendata_put(opendata);
1662
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
1663 1664
}

1665
static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state)
1666
{
1667
	struct nfs_server *server = NFS_SERVER(state->inode);
1668 1669 1670 1671
	struct nfs4_exception exception = { };
	int err;

	do {
1672
		err = _nfs4_open_expired(ctx, state);
1673 1674 1675 1676 1677 1678 1679 1680
		switch (err) {
		default:
			goto out;
		case -NFS4ERR_GRACE:
		case -NFS4ERR_DELAY:
			nfs4_handle_exception(server, err, &exception);
			err = 0;
		}
1681
	} while (exception.retry);
1682
out:
1683 1684 1685
	return err;
}

Linus Torvalds's avatar
Linus Torvalds committed
1686 1687 1688
static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
	struct nfs_open_context *ctx;
1689
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
1690

1691 1692 1693
	ctx = nfs4_state_find_open_context(state);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);
1694
	ret = nfs4_do_open_expired(ctx, state);
1695 1696
	put_nfs_open_context(ctx);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
1697 1698
}

1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712
#if defined(CONFIG_NFS_V4_1)
static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
	int status;
	struct nfs_server *server = NFS_SERVER(state->inode);

	status = nfs41_test_stateid(server, state);
	if (status == NFS_OK)
		return 0;
	nfs41_free_stateid(server, state);
	return nfs4_open_expired(sp, state);
}
#endif

1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728
/*
 * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-*
 * fields corresponding to attributes that were used to store the verifier.
 * Make sure we clobber those fields in the later setattr call
 */
static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct iattr *sattr)
{
	if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_ACCESS) &&
	    !(sattr->ia_valid & ATTR_ATIME_SET))
		sattr->ia_valid |= ATTR_ATIME;

	if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_MODIFY) &&
	    !(sattr->ia_valid & ATTR_MTIME_SET))
		sattr->ia_valid |= ATTR_MTIME;
}

Linus Torvalds's avatar
Linus Torvalds committed
1729
/*
1730
 * Returns a referenced nfs4_state
Linus Torvalds's avatar
Linus Torvalds committed
1731
 */
1732
static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
Linus Torvalds's avatar
Linus Torvalds committed
1733 1734 1735 1736
{
	struct nfs4_state_owner  *sp;
	struct nfs4_state     *state = NULL;
	struct nfs_server       *server = NFS_SERVER(dir);
1737
	struct nfs4_opendata *opendata;
1738
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
1739 1740 1741 1742 1743 1744 1745

	/* Protect against reboot recovery conflicts */
	status = -ENOMEM;
	if (!(sp = nfs4_get_state_owner(server, cred))) {
		dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
		goto out_err;
	}
1746 1747
	status = nfs4_recover_expired_lease(server);
	if (status != 0)
1748
		goto err_put_state_owner;
1749 1750
	if (dentry->d_inode != NULL)
		nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
1751
	status = -ENOMEM;
1752
	opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, GFP_KERNEL);
1753
	if (opendata == NULL)
1754
		goto err_put_state_owner;
Linus Torvalds's avatar
Linus Torvalds committed
1755

1756 1757
	if (dentry->d_inode != NULL)
		opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
1758

1759
	status = _nfs4_proc_open(opendata);
Linus Torvalds's avatar
Linus Torvalds committed
1760
	if (status != 0)
1761
		goto err_opendata_put;
Linus Torvalds's avatar
Linus Torvalds committed
1762

1763
	state = nfs4_opendata_to_nfs4_state(opendata);
1764 1765
	status = PTR_ERR(state);
	if (IS_ERR(state))
1766
		goto err_opendata_put;
Trond Myklebust's avatar
Trond Myklebust committed
1767
	if (server->caps & NFS_CAP_POSIX_LOCK)
1768
		set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780

	if (opendata->o_arg.open_flags & O_EXCL) {
		nfs4_exclusive_attrset(opendata, sattr);

		nfs_fattr_init(opendata->o_res.f_attr);
		status = nfs4_do_setattr(state->inode, cred,
				opendata->o_res.f_attr, sattr,
				state);
		if (status == 0)
			nfs_setattr_update_inode(state->inode, sattr);
		nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
	}
1781
	nfs4_opendata_put(opendata);
Linus Torvalds's avatar
Linus Torvalds committed
1782 1783 1784
	nfs4_put_state_owner(sp);
	*res = state;
	return 0;
1785 1786
err_opendata_put:
	nfs4_opendata_put(opendata);
1787 1788
err_put_state_owner:
	nfs4_put_state_owner(sp);
Linus Torvalds's avatar
Linus Torvalds committed
1789 1790 1791 1792 1793 1794
out_err:
	*res = NULL;
	return status;
}


1795
static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
Linus Torvalds's avatar
Linus Torvalds committed
1796 1797 1798 1799 1800 1801
{
	struct nfs4_exception exception = { };
	struct nfs4_state *res;
	int status;

	do {
1802
		status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res);
Linus Torvalds's avatar
Linus Torvalds committed
1803 1804 1805 1806 1807 1808 1809 1810
		if (status == 0)
			break;
		/* NOTE: BAD_SEQID means the server and client disagree about the
		 * book-keeping w.r.t. state-changing operations
		 * (OPEN/CLOSE/LOCK/LOCKU...)
		 * It is actually a sign of a bug on the client or on the server.
		 *
		 * If we receive a BAD_SEQID error in the particular case of
1811
		 * doing an OPEN, we assume that nfs_increment_open_seqid() will
Linus Torvalds's avatar
Linus Torvalds committed
1812 1813 1814 1815 1816
		 * have unhashed the old state_owner for us, and that we can
		 * therefore safely retry using a new one. We should still warn
		 * the user though...
		 */
		if (status == -NFS4ERR_BAD_SEQID) {
1817 1818 1819
			printk(KERN_WARNING "NFS: v4 server %s "
					" returned a bad sequence-id error!\n",
					NFS_SERVER(dir)->nfs_client->cl_hostname);
Linus Torvalds's avatar
Linus Torvalds committed
1820 1821 1822
			exception.retry = 1;
			continue;
		}
1823 1824 1825 1826 1827 1828 1829 1830 1831 1832
		/*
		 * BAD_STATEID on OPEN means that the server cancelled our
		 * state before it received the OPEN_CONFIRM.
		 * Recover by retrying the request as per the discussion
		 * on Page 181 of RFC3530.
		 */
		if (status == -NFS4ERR_BAD_STATEID) {
			exception.retry = 1;
			continue;
		}
1833 1834 1835 1836 1837
		if (status == -EAGAIN) {
			/* We must have found a delegation */
			exception.retry = 1;
			continue;
		}
Linus Torvalds's avatar
Linus Torvalds committed
1838 1839 1840 1841 1842 1843
		res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
					status, &exception));
	} while (exception.retry);
	return res;
}

1844 1845 1846
static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs4_state *state)
Linus Torvalds's avatar
Linus Torvalds committed
1847
{
1848
	struct nfs_server *server = NFS_SERVER(inode);
Linus Torvalds's avatar
Linus Torvalds committed
1849
        struct nfs_setattrargs  arg = {
1850
                .fh             = NFS_FH(inode),
Linus Torvalds's avatar
Linus Torvalds committed
1851 1852 1853 1854 1855 1856 1857 1858 1859
                .iap            = sattr,
		.server		= server,
		.bitmask = server->attr_bitmask,
        };
        struct nfs_setattrres  res = {
		.fattr		= fattr,
		.server		= server,
        };
        struct rpc_message msg = {
1860 1861 1862 1863
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
		.rpc_argp	= &arg,
		.rpc_resp	= &res,
		.rpc_cred	= cred,
Linus Torvalds's avatar
Linus Torvalds committed
1864
        };
1865
	unsigned long timestamp = jiffies;
1866
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
1867

1868
	nfs_fattr_init(fattr);
Linus Torvalds's avatar
Linus Torvalds committed
1869

1870 1871 1872
	if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) {
		/* Use that stateid */
	} else if (state != NULL) {
1873
		nfs4_copy_stateid(&arg.stateid, state, current->files, current->tgid);
1874
	} else
Linus Torvalds's avatar
Linus Torvalds committed
1875 1876
		memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));

1877
	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
1878 1879
	if (status == 0 && state != NULL)
		renew_lease(server, timestamp);
1880
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
1881 1882
}

1883 1884 1885
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
			   struct nfs_fattr *fattr, struct iattr *sattr,
			   struct nfs4_state *state)
Linus Torvalds's avatar
Linus Torvalds committed
1886
{
1887
	struct nfs_server *server = NFS_SERVER(inode);
Linus Torvalds's avatar
Linus Torvalds committed
1888 1889 1890 1891
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(server,
1892
				_nfs4_do_setattr(inode, cred, fattr, sattr, state),
Linus Torvalds's avatar
Linus Torvalds committed
1893 1894 1895 1896 1897 1898 1899 1900 1901 1902
				&exception);
	} while (exception.retry);
	return err;
}

struct nfs4_closedata {
	struct inode *inode;
	struct nfs4_state *state;
	struct nfs_closeargs arg;
	struct nfs_closeres res;
1903
	struct nfs_fattr fattr;
1904
	unsigned long timestamp;
Fred Isaman's avatar
Fred Isaman committed
1905 1906
	bool roc;
	u32 roc_barrier;
Linus Torvalds's avatar
Linus Torvalds committed
1907 1908
};

1909
static void nfs4_free_closedata(void *data)
1910
{
1911 1912
	struct nfs4_closedata *calldata = data;
	struct nfs4_state_owner *sp = calldata->state->owner;
1913
	struct super_block *sb = calldata->state->inode->i_sb;
1914

Fred Isaman's avatar
Fred Isaman committed
1915 1916
	if (calldata->roc)
		pnfs_roc_release(calldata->state->inode);
1917 1918 1919
	nfs4_put_open_state(calldata->state);
	nfs_free_seqid(calldata->arg.seqid);
	nfs4_put_state_owner(sp);
1920
	nfs_sb_deactive(sb);
1921 1922 1923
	kfree(calldata);
}

1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935
static void nfs4_close_clear_stateid_flags(struct nfs4_state *state,
		fmode_t fmode)
{
	spin_lock(&state->owner->so_lock);
	if (!(fmode & FMODE_READ))
		clear_bit(NFS_O_RDONLY_STATE, &state->flags);
	if (!(fmode & FMODE_WRITE))
		clear_bit(NFS_O_WRONLY_STATE, &state->flags);
	clear_bit(NFS_O_RDWR_STATE, &state->flags);
	spin_unlock(&state->owner->so_lock);
}

1936
static void nfs4_close_done(struct rpc_task *task, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
1937
{
1938
	struct nfs4_closedata *calldata = data;
Linus Torvalds's avatar
Linus Torvalds committed
1939 1940 1941
	struct nfs4_state *state = calldata->state;
	struct nfs_server *server = NFS_SERVER(calldata->inode);

1942 1943
	if (!nfs4_sequence_done(task, &calldata->res.seq_res))
		return;
Linus Torvalds's avatar
Linus Torvalds committed
1944 1945 1946 1947 1948
        /* hmm. we are done with the inode, and in the process of freeing
	 * the state_owner. we keep this around to process errors
	 */
	switch (task->tk_status) {
		case 0:
Fred Isaman's avatar
Fred Isaman committed
1949 1950 1951
			if (calldata->roc)
				pnfs_roc_set_barrier(state->inode,
						     calldata->roc_barrier);
1952
			nfs_set_open_stateid(state, &calldata->res.stateid, 0);
1953
			renew_lease(server, calldata->timestamp);
1954 1955
			nfs4_close_clear_stateid_flags(state,
					calldata->arg.fmode);
Linus Torvalds's avatar
Linus Torvalds committed
1956 1957
			break;
		case -NFS4ERR_STALE_STATEID:
1958 1959
		case -NFS4ERR_OLD_STATEID:
		case -NFS4ERR_BAD_STATEID:
Linus Torvalds's avatar
Linus Torvalds committed
1960
		case -NFS4ERR_EXPIRED:
1961
			if (calldata->arg.fmode == 0)
1962
				break;
Linus Torvalds's avatar
Linus Torvalds committed
1963
		default:
1964 1965
			if (nfs4_async_handle_error(task, server, state) == -EAGAIN)
				rpc_restart_call_prepare(task);
Linus Torvalds's avatar
Linus Torvalds committed
1966
	}
1967
	nfs_release_seqid(calldata->arg.seqid);
1968
	nfs_refresh_inode(calldata->inode, calldata->res.fattr);
Linus Torvalds's avatar
Linus Torvalds committed
1969 1970
}

Trond Myklebust's avatar
Trond Myklebust committed
1971
static void nfs4_close_prepare(struct rpc_task *task, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
1972
{
Trond Myklebust's avatar
Trond Myklebust committed
1973
	struct nfs4_closedata *calldata = data;
1974
	struct nfs4_state *state = calldata->state;
1975
	int call_close = 0;
1976

1977
	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
1978
		return;
1979

1980 1981
	task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
	calldata->arg.fmode = FMODE_READ|FMODE_WRITE;
1982
	spin_lock(&state->owner->so_lock);
1983
	/* Calculate the change in open mode */
1984
	if (state->n_rdwr == 0) {
1985
		if (state->n_rdonly == 0) {
1986 1987 1988
			call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
			call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
			calldata->arg.fmode &= ~FMODE_READ;
1989 1990
		}
		if (state->n_wronly == 0) {
1991 1992 1993
			call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
			call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
			calldata->arg.fmode &= ~FMODE_WRITE;
1994
		}
1995
	}
1996
	spin_unlock(&state->owner->so_lock);
1997 1998

	if (!call_close) {
1999 2000
		/* Note: exit _without_ calling nfs4_close_done */
		task->tk_action = NULL;
2001 2002
		return;
	}
2003

Fred Isaman's avatar
Fred Isaman committed
2004
	if (calldata->arg.fmode == 0) {
2005
		task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
Fred Isaman's avatar
Fred Isaman committed
2006 2007 2008 2009 2010 2011 2012
		if (calldata->roc &&
		    pnfs_roc_drain(calldata->inode, &calldata->roc_barrier)) {
			rpc_sleep_on(&NFS_SERVER(calldata->inode)->roc_rpcwaitq,
				     task, NULL);
			return;
		}
	}
2013

2014
	nfs_fattr_init(calldata->res.fattr);
2015
	calldata->timestamp = jiffies;
2016
	if (nfs4_setup_sequence(NFS_SERVER(calldata->inode),
2017 2018 2019
				&calldata->arg.seq_args, &calldata->res.seq_res,
				1, task))
		return;
2020
	rpc_call_start(task);
Linus Torvalds's avatar
Linus Torvalds committed
2021 2022
}

2023
static const struct rpc_call_ops nfs4_close_ops = {
Trond Myklebust's avatar
Trond Myklebust committed
2024
	.rpc_call_prepare = nfs4_close_prepare,
2025 2026 2027 2028
	.rpc_call_done = nfs4_close_done,
	.rpc_release = nfs4_free_closedata,
};

Linus Torvalds's avatar
Linus Torvalds committed
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
/* 
 * It is possible for data to be read/written from a mem-mapped file 
 * after the sys_close call (which hits the vfs layer as a flush).
 * This means that we can't safely call nfsv4 close on a file until 
 * the inode is cleared. This in turn means that we are not good
 * NFSv4 citizens - we do not indicate to the server to update the file's 
 * share state even when we are done with one of the three share 
 * stateid's in the inode.
 *
 * NOTE: Caller must be holding the sp->so_owner semaphore!
 */
2040
int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc)
Linus Torvalds's avatar
Linus Torvalds committed
2041
{
2042
	struct nfs_server *server = NFS_SERVER(state->inode);
Linus Torvalds's avatar
Linus Torvalds committed
2043
	struct nfs4_closedata *calldata;
2044 2045
	struct nfs4_state_owner *sp = state->owner;
	struct rpc_task *task;
2046 2047 2048 2049
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
		.rpc_cred = state->owner->so_cred,
	};
2050 2051
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
2052
		.rpc_message = &msg,
2053
		.callback_ops = &nfs4_close_ops,
2054
		.workqueue = nfsiod_workqueue,
2055 2056
		.flags = RPC_TASK_ASYNC,
	};
2057
	int status = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
2058

2059
	calldata = kzalloc(sizeof(*calldata), gfp_mask);
Linus Torvalds's avatar
Linus Torvalds committed
2060
	if (calldata == NULL)
2061
		goto out;
2062
	calldata->inode = state->inode;
Linus Torvalds's avatar
Linus Torvalds committed
2063
	calldata->state = state;
2064
	calldata->arg.fh = NFS_FH(state->inode);
2065
	calldata->arg.stateid = &state->open_stateid;
Linus Torvalds's avatar
Linus Torvalds committed
2066
	/* Serialization for the sequence id */
2067
	calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask);
2068 2069
	if (calldata->arg.seqid == NULL)
		goto out_free_calldata;
2070
	calldata->arg.fmode = 0;
2071
	calldata->arg.bitmask = server->cache_consistency_bitmask;
2072
	calldata->res.fattr = &calldata->fattr;
2073
	calldata->res.seqid = calldata->arg.seqid;
2074
	calldata->res.server = server;
Fred Isaman's avatar
Fred Isaman committed
2075
	calldata->roc = roc;
2076
	nfs_sb_active(calldata->inode->i_sb);
2077

2078 2079
	msg.rpc_argp = &calldata->arg;
	msg.rpc_resp = &calldata->res;
2080 2081
	task_setup_data.callback_data = calldata;
	task = rpc_run_task(&task_setup_data);
2082 2083
	if (IS_ERR(task))
		return PTR_ERR(task);
2084 2085 2086
	status = 0;
	if (wait)
		status = rpc_wait_for_completion_task(task);
2087
	rpc_put_task(task);
2088
	return status;
2089 2090 2091
out_free_calldata:
	kfree(calldata);
out:
Fred Isaman's avatar
Fred Isaman committed
2092 2093
	if (roc)
		pnfs_roc_release(state->inode);
2094 2095
	nfs4_put_open_state(state);
	nfs4_put_state_owner(sp);
2096
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
2097 2098
}

2099
static struct inode *
2100
nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
Linus Torvalds's avatar
Linus Torvalds committed
2101 2102 2103
{
	struct nfs4_state *state;

2104
	/* Protect against concurrent sillydeletes */
2105
	state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, ctx->cred);
2106 2107
	if (IS_ERR(state))
		return ERR_CAST(state);
2108
	ctx->state = state;
2109
	return igrab(state->inode);
Linus Torvalds's avatar
Linus Torvalds committed
2110 2111
}

2112
static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
Trond Myklebust's avatar
Trond Myklebust committed
2113 2114 2115 2116
{
	if (ctx->state == NULL)
		return;
	if (is_sync)
2117
		nfs4_close_sync(ctx->state, ctx->mode);
Trond Myklebust's avatar
Trond Myklebust committed
2118
	else
2119
		nfs4_close_state(ctx->state, ctx->mode);
Trond Myklebust's avatar
Trond Myklebust committed
2120
}
Linus Torvalds's avatar
Linus Torvalds committed
2121 2122 2123

static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
{
Benny Halevy's avatar
Benny Halevy committed
2124 2125 2126
	struct nfs4_server_caps_arg args = {
		.fhandle = fhandle,
	};
Linus Torvalds's avatar
Linus Torvalds committed
2127 2128 2129
	struct nfs4_server_caps_res res = {};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS],
Benny Halevy's avatar
Benny Halevy committed
2130
		.rpc_argp = &args,
Linus Torvalds's avatar
Linus Torvalds committed
2131 2132 2133 2134
		.rpc_resp = &res,
	};
	int status;

2135
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2136 2137
	if (status == 0) {
		memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
2138 2139 2140 2141 2142
		server->caps &= ~(NFS_CAP_ACLS|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);
Linus Torvalds's avatar
Linus Torvalds committed
2143 2144 2145 2146 2147 2148
		if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
			server->caps |= NFS_CAP_ACLS;
		if (res.has_links != 0)
			server->caps |= NFS_CAP_HARDLINKS;
		if (res.has_symlinks != 0)
			server->caps |= NFS_CAP_SYMLINKS;
2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165
		if (res.attr_bitmask[0] & FATTR4_WORD0_FILEID)
			server->caps |= NFS_CAP_FILEID;
		if (res.attr_bitmask[1] & FATTR4_WORD1_MODE)
			server->caps |= NFS_CAP_MODE;
		if (res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS)
			server->caps |= NFS_CAP_NLINK;
		if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER)
			server->caps |= NFS_CAP_OWNER;
		if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER_GROUP)
			server->caps |= NFS_CAP_OWNER_GROUP;
		if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_ACCESS)
			server->caps |= NFS_CAP_ATIME;
		if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_METADATA)
			server->caps |= NFS_CAP_CTIME;
		if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
			server->caps |= NFS_CAP_MTIME;

2166 2167 2168
		memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
		server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
		server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
Linus Torvalds's avatar
Linus Torvalds committed
2169 2170
		server->acl_bitmask = res.acl_bitmask;
	}
Andy Adamson's avatar
Andy Adamson committed
2171

Linus Torvalds's avatar
Linus Torvalds committed
2172 2173 2174
	return status;
}

2175
int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
Linus Torvalds's avatar
Linus Torvalds committed
2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(server,
				_nfs4_server_capabilities(server, fhandle),
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
		struct nfs_fsinfo *info)
{
	struct nfs4_lookup_root_arg args = {
		.bitmask = nfs4_fattr_bitmap,
	};
	struct nfs4_lookup_res res = {
		.server = server,
2195
		.fattr = info->fattr,
Linus Torvalds's avatar
Linus Torvalds committed
2196 2197 2198 2199 2200 2201 2202
		.fh = fhandle,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP_ROOT],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
2203

2204
	nfs_fattr_init(info->fattr);
2205
	return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2206 2207 2208 2209 2210 2211 2212 2213
}

static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
		struct nfs_fsinfo *info)
{
	struct nfs4_exception exception = { };
	int err;
	do {
2214 2215 2216 2217 2218 2219 2220 2221
		err = _nfs4_lookup_root(server, fhandle, info);
		switch (err) {
		case 0:
		case -NFS4ERR_WRONGSEC:
			break;
		default:
			err = nfs4_handle_exception(server, err, &exception);
		}
Linus Torvalds's avatar
Linus Torvalds committed
2222 2223 2224 2225
	} while (exception.retry);
	return err;
}

2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241
static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
				struct nfs_fsinfo *info, rpc_authflavor_t flavor)
{
	struct rpc_auth *auth;
	int ret;

	auth = rpcauth_create(flavor, server->client);
	if (!auth) {
		ret = -EIO;
		goto out;
	}
	ret = nfs4_lookup_root(server, fhandle, info);
out:
	return ret;
}

2242
static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
2243
			      struct nfs_fsinfo *info)
Linus Torvalds's avatar
Linus Torvalds committed
2244
{
2245
	int i, len, status = 0;
2246
	rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
Linus Torvalds's avatar
Linus Torvalds committed
2247

2248 2249 2250
	len = gss_mech_list_pseudoflavors(&flav_array[0]);
	flav_array[len] = RPC_AUTH_NULL;
	len += 1;
2251 2252 2253

	for (i = 0; i < len; i++) {
		status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
2254
		if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
2255 2256
			continue;
		break;
2257
	}
2258 2259 2260 2261 2262 2263 2264 2265 2266
	/*
	 * -EACCESS could mean that the user doesn't have correct permissions
	 * to access the mount.  It could also mean that we tried to mount
	 * with a gss auth flavor, but rpc.gssd isn't running.  Either way,
	 * existing mount programs don't handle -EACCES very well so it should
	 * be mapped to -EPERM instead.
	 */
	if (status == -EACCES)
		status = -EPERM;
2267 2268 2269 2270 2271 2272 2273 2274 2275
	return status;
}

/*
 * get the file handle for the "/" directory on the server
 */
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
			      struct nfs_fsinfo *info)
{
2276
	int minor_version = server->nfs_client->cl_minorversion;
2277
	int status = nfs4_lookup_root(server, fhandle, info);
2278 2279 2280 2281 2282
	if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
		/*
		 * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
		 * by nfs4_map_errors() as this function exits.
		 */
2283
		status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
Linus Torvalds's avatar
Linus Torvalds committed
2284 2285 2286 2287
	if (status == 0)
		status = nfs4_server_capabilities(server, fhandle);
	if (status == 0)
		status = nfs4_do_fsinfo(server, fhandle, info);
2288
	return nfs4_map_errors(status);
Linus Torvalds's avatar
Linus Torvalds committed
2289 2290
}

2291
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
Manoj Naik's avatar
Manoj Naik committed
2292 2293 2294 2295 2296
/*
 * Get locations and (maybe) other attributes of a referral.
 * Note that we'll actually follow the referral later when
 * we detect fsid mismatch in inode revalidation
 */
2297 2298
static int nfs4_get_referral(struct inode *dir, const struct qstr *name,
			     struct nfs_fattr *fattr, struct nfs_fh *fhandle)
Manoj Naik's avatar
Manoj Naik committed
2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
{
	int status = -ENOMEM;
	struct page *page = NULL;
	struct nfs4_fs_locations *locations = NULL;

	page = alloc_page(GFP_KERNEL);
	if (page == NULL)
		goto out;
	locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
	if (locations == NULL)
		goto out;

2311
	status = nfs4_proc_fs_locations(dir, name, locations, page);
Manoj Naik's avatar
Manoj Naik committed
2312 2313 2314 2315
	if (status != 0)
		goto out;
	/* Make sure server returned a different fsid for the referral */
	if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
2316 2317
		dprintk("%s: server did not return a different fsid for"
			" a referral at %s\n", __func__, name->name);
Manoj Naik's avatar
Manoj Naik committed
2318 2319 2320
		status = -EIO;
		goto out;
	}
2321 2322
	/* Fixup attributes for the nfs_lookup() call to nfs_fhget() */
	nfs_fixup_referral_attributes(&locations->fattr);
Manoj Naik's avatar
Manoj Naik committed
2323

2324
	/* replace the lookup nfs_fattr with the locations nfs_fattr */
Manoj Naik's avatar
Manoj Naik committed
2325 2326 2327 2328 2329
	memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
	memset(fhandle, 0, sizeof(struct nfs_fh));
out:
	if (page)
		__free_page(page);
2330
	kfree(locations);
Manoj Naik's avatar
Manoj Naik committed
2331 2332 2333
	return status;
}

Linus Torvalds's avatar
Linus Torvalds committed
2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
	struct nfs4_getattr_arg args = {
		.fh = fhandle,
		.bitmask = server->attr_bitmask,
	};
	struct nfs4_getattr_res res = {
		.fattr = fattr,
		.server = server,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	
2350
	nfs_fattr_init(fattr);
2351
	return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386
}

static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(server,
				_nfs4_proc_getattr(server, fhandle, fattr),
				&exception);
	} while (exception.retry);
	return err;
}

/* 
 * The file is not closed if it is opened due to the a request to change
 * the size of the file. The open call will not be needed once the
 * VFS layer lookup-intents are implemented.
 *
 * Close is called when the inode is destroyed.
 * If we haven't opened the file for O_WRONLY, we
 * need to in the size_change case to obtain a stateid.
 *
 * Got race?
 * Because OPEN is always done by name in nfsv4, it is
 * possible that we opened a different file by the same
 * name.  We can recognize this race condition, but we
 * can't do anything about it besides returning an error.
 *
 * This will be fixed with VFS changes (lookup-intent).
 */
static int
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
		  struct iattr *sattr)
{
2387
	struct inode *inode = dentry->d_inode;
2388
	struct rpc_cred *cred = NULL;
2389
	struct nfs4_state *state = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2390 2391
	int status;

Benny Halevy's avatar
Benny Halevy committed
2392 2393 2394
	if (pnfs_ld_layoutret_on_setattr(inode))
		pnfs_return_layout(inode);

2395
	nfs_fattr_init(fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2396
	
2397
	/* Search for an existing open(O_WRITE) file */
2398 2399 2400 2401
	if (sattr->ia_valid & ATTR_FILE) {
		struct nfs_open_context *ctx;

		ctx = nfs_file_open_context(sattr->ia_file);
Neil Brown's avatar
Neil Brown committed
2402 2403 2404 2405
		if (ctx) {
			cred = ctx->cred;
			state = ctx->state;
		}
2406
	}
2407

2408
	status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
2409 2410
	if (status == 0)
		nfs_setattr_update_inode(inode, sattr);
Linus Torvalds's avatar
Linus Torvalds committed
2411 2412 2413
	return status;
}

2414 2415 2416
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
		const struct qstr *name, struct nfs_fh *fhandle,
		struct nfs_fattr *fattr)
David Howells's avatar
David Howells committed
2417
{
2418
	struct nfs_server *server = NFS_SERVER(dir);
David Howells's avatar
David Howells committed
2419 2420 2421
	int		       status;
	struct nfs4_lookup_arg args = {
		.bitmask = server->attr_bitmask,
2422
		.dir_fh = NFS_FH(dir),
David Howells's avatar
David Howells committed
2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437
		.name = name,
	};
	struct nfs4_lookup_res res = {
		.server = server,
		.fattr = fattr,
		.fh = fhandle,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};

	nfs_fattr_init(fattr);

Linus Torvalds's avatar
Linus Torvalds committed
2438
	dprintk("NFS call  lookup %s\n", name->name);
2439
	status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2440 2441 2442 2443
	dprintk("NFS reply lookup: %d\n", status);
	return status;
}

2444 2445 2446 2447 2448 2449 2450 2451 2452 2453
void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh)
{
	memset(fh, 0, sizeof(struct nfs_fh));
	fattr->fsid.major = 1;
	fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
		NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_FSID | NFS_ATTR_FATTR_MOUNTPOINT;
	fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
	fattr->nlink = 2;
}

2454 2455
static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
			    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
Linus Torvalds's avatar
Linus Torvalds committed
2456 2457 2458 2459
{
	struct nfs4_exception exception = { };
	int err;
	do {
2460 2461 2462 2463
		int status;

		status = _nfs4_proc_lookup(clnt, dir, name, fhandle, fattr);
		switch (status) {
2464 2465
		case -NFS4ERR_BADNAME:
			return -ENOENT;
2466
		case -NFS4ERR_MOVED:
2467
			return nfs4_get_referral(dir, name, fattr, fhandle);
2468
		case -NFS4ERR_WRONGSEC:
2469
			nfs_fixup_secinfo_attributes(fattr, fhandle);
2470 2471 2472
		}
		err = nfs4_handle_exception(NFS_SERVER(dir),
				status, &exception);
Linus Torvalds's avatar
Linus Torvalds committed
2473 2474 2475 2476 2477 2478
	} while (exception.retry);
	return err;
}

static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
2479
	struct nfs_server *server = NFS_SERVER(inode);
Linus Torvalds's avatar
Linus Torvalds committed
2480 2481
	struct nfs4_accessargs args = {
		.fh = NFS_FH(inode),
2482 2483 2484 2485
		.bitmask = server->attr_bitmask,
	};
	struct nfs4_accessres res = {
		.server = server,
Linus Torvalds's avatar
Linus Torvalds committed
2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_cred = entry->cred,
	};
	int mode = entry->mask;
	int status;

	/*
	 * Determine which access bits we want to ask for...
	 */
	if (mode & MAY_READ)
		args.access |= NFS4_ACCESS_READ;
	if (S_ISDIR(inode->i_mode)) {
		if (mode & MAY_WRITE)
			args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE;
		if (mode & MAY_EXEC)
			args.access |= NFS4_ACCESS_LOOKUP;
	} else {
		if (mode & MAY_WRITE)
			args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND;
		if (mode & MAY_EXEC)
			args.access |= NFS4_ACCESS_EXECUTE;
	}
2512 2513 2514 2515 2516

	res.fattr = nfs_alloc_fattr();
	if (res.fattr == NULL)
		return -ENOMEM;

2517
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2518 2519 2520 2521 2522 2523 2524 2525
	if (!status) {
		entry->mask = 0;
		if (res.access & NFS4_ACCESS_READ)
			entry->mask |= MAY_READ;
		if (res.access & (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
			entry->mask |= MAY_WRITE;
		if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
			entry->mask |= MAY_EXEC;
2526
		nfs_refresh_inode(inode, res.fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2527
	}
2528
	nfs_free_fattr(res.fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576
	return status;
}

static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(inode),
				_nfs4_proc_access(inode, entry),
				&exception);
	} while (exception.retry);
	return err;
}

/*
 * TODO: For the time being, we don't try to get any attributes
 * along with any of the zero-copy operations READ, READDIR,
 * READLINK, WRITE.
 *
 * In the case of the first three, we want to put the GETATTR
 * after the read-type operation -- this is because it is hard
 * to predict the length of a GETATTR response in v4, and thus
 * align the READ data correctly.  This means that the GETATTR
 * may end up partially falling into the page cache, and we should
 * shift it into the 'tail' of the xdr_buf before processing.
 * To do this efficiently, we need to know the total length
 * of data received, which doesn't seem to be available outside
 * of the RPC layer.
 *
 * In the case of WRITE, we also want to put the GETATTR after
 * the operation -- in this case because we want to make sure
 * we get the post-operation mtime and size.  This means that
 * we can't use xdr_encode_pages() as written: we need a variant
 * of it which would leave room in the 'tail' iovec.
 *
 * Both of these changes to the XDR layer would in fact be quite
 * minor, but I decided to leave them for a subsequent patch.
 */
static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
		unsigned int pgbase, unsigned int pglen)
{
	struct nfs4_readlink args = {
		.fh       = NFS_FH(inode),
		.pgbase	  = pgbase,
		.pglen    = pglen,
		.pages    = &page,
	};
Benny Halevy's avatar
Benny Halevy committed
2577
	struct nfs4_readlink_res res;
Linus Torvalds's avatar
Linus Torvalds committed
2578 2579 2580
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK],
		.rpc_argp = &args,
Benny Halevy's avatar
Benny Halevy committed
2581
		.rpc_resp = &res,
Linus Torvalds's avatar
Linus Torvalds committed
2582 2583
	};

2584
	return nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616
}

static int nfs4_proc_readlink(struct inode *inode, struct page *page,
		unsigned int pgbase, unsigned int pglen)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(inode),
				_nfs4_proc_readlink(inode, page, pgbase, pglen),
				&exception);
	} while (exception.retry);
	return err;
}

/*
 * Got race?
 * We will need to arrange for the VFS layer to provide an atomic open.
 * Until then, this create/open method is prone to inefficiency and race
 * conditions due to the lookup, create, and open VFS calls from sys_open()
 * placed on the wire.
 *
 * Given the above sorry state of affairs, I'm simply sending an OPEN.
 * The file will be opened again in the subsequent VFS open call
 * (nfs4_proc_file_open).
 *
 * The open for read will just hang around to be used by any process that
 * opens the file O_RDONLY. This will all be resolved with the VFS changes.
 */

static int
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
2617
                 int flags, struct nfs_open_context *ctx)
Linus Torvalds's avatar
Linus Torvalds committed
2618
{
2619
	struct dentry *de = dentry;
Linus Torvalds's avatar
Linus Torvalds committed
2620
	struct nfs4_state *state;
2621 2622
	struct rpc_cred *cred = NULL;
	fmode_t fmode = 0;
Linus Torvalds's avatar
Linus Torvalds committed
2623 2624
	int status = 0;

2625 2626
	if (ctx != NULL) {
		cred = ctx->cred;
2627
		de = ctx->dentry;
2628
		fmode = ctx->mode;
Linus Torvalds's avatar
Linus Torvalds committed
2629
	}
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
2630
	sattr->ia_mode &= ~current_umask();
2631
	state = nfs4_do_open(dir, de, fmode, flags, sattr, cred);
2632
	d_drop(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
2633 2634
	if (IS_ERR(state)) {
		status = PTR_ERR(state);
2635
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
2636
	}
2637
	d_add(dentry, igrab(state->inode));
2638
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
2639 2640
	if (ctx != NULL)
		ctx->state = state;
2641
	else
2642
		nfs4_close_sync(state, fmode);
Linus Torvalds's avatar
Linus Torvalds committed
2643 2644 2645 2646 2647 2648
out:
	return status;
}

static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
2649
	struct nfs_server *server = NFS_SERVER(dir);
2650
	struct nfs_removeargs args = {
Linus Torvalds's avatar
Linus Torvalds committed
2651
		.fh = NFS_FH(dir),
2652 2653
		.name.len = name->len,
		.name.name = name->name,
2654 2655
		.bitmask = server->attr_bitmask,
	};
2656
	struct nfs_removeres res = {
2657
		.server = server,
Linus Torvalds's avatar
Linus Torvalds committed
2658 2659
	};
	struct rpc_message msg = {
2660 2661 2662
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
		.rpc_argp = &args,
		.rpc_resp = &res,
Linus Torvalds's avatar
Linus Torvalds committed
2663
	};
2664 2665 2666 2667 2668
	int status = -ENOMEM;

	res.dir_attr = nfs_alloc_fattr();
	if (res.dir_attr == NULL)
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
2669

2670
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
2671 2672
	if (status == 0) {
		update_changeattr(dir, &res.cinfo);
2673
		nfs_post_op_update_inode(dir, res.dir_attr);
2674
	}
2675 2676
	nfs_free_fattr(res.dir_attr);
out:
Linus Torvalds's avatar
Linus Torvalds committed
2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691
	return status;
}

static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(dir),
				_nfs4_proc_remove(dir, name),
				&exception);
	} while (exception.retry);
	return err;
}

2692
static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
Linus Torvalds's avatar
Linus Torvalds committed
2693
{
2694 2695 2696
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs_removeargs *args = msg->rpc_argp;
	struct nfs_removeres *res = msg->rpc_resp;
Linus Torvalds's avatar
Linus Torvalds committed
2697

2698
	args->bitmask = server->cache_consistency_bitmask;
2699
	res->server = server;
2700
	res->seq_res.sr_slot = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2701 2702 2703
	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
}

2704
static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
Linus Torvalds's avatar
Linus Torvalds committed
2705
{
2706 2707
	struct nfs_removeres *res = task->tk_msg.rpc_resp;

2708 2709
	if (!nfs4_sequence_done(task, &res->seq_res))
		return 0;
2710
	if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
2711 2712
		return 0;
	update_changeattr(dir, &res->cinfo);
2713
	nfs_post_op_update_inode(dir, res->dir_attr);
2714
	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
2715 2716
}

2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744
static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
{
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs_renameargs *arg = msg->rpc_argp;
	struct nfs_renameres *res = msg->rpc_resp;

	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
	arg->bitmask = server->attr_bitmask;
	res->server = server;
}

static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
				 struct inode *new_dir)
{
	struct nfs_renameres *res = task->tk_msg.rpc_resp;

	if (!nfs4_sequence_done(task, &res->seq_res))
		return 0;
	if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
		return 0;

	update_changeattr(old_dir, &res->old_cinfo);
	nfs_post_op_update_inode(old_dir, res->old_fattr);
	update_changeattr(new_dir, &res->new_cinfo);
	nfs_post_op_update_inode(new_dir, res->new_fattr);
	return 1;
}

Linus Torvalds's avatar
Linus Torvalds committed
2745 2746 2747
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
		struct inode *new_dir, struct qstr *new_name)
{
2748
	struct nfs_server *server = NFS_SERVER(old_dir);
2749
	struct nfs_renameargs arg = {
Linus Torvalds's avatar
Linus Torvalds committed
2750 2751 2752 2753
		.old_dir = NFS_FH(old_dir),
		.new_dir = NFS_FH(new_dir),
		.old_name = old_name,
		.new_name = new_name,
2754 2755
		.bitmask = server->attr_bitmask,
	};
2756
	struct nfs_renameres res = {
2757
		.server = server,
Linus Torvalds's avatar
Linus Torvalds committed
2758 2759 2760 2761 2762 2763
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME],
		.rpc_argp = &arg,
		.rpc_resp = &res,
	};
2764
	int status = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
2765
	
2766 2767 2768 2769
	res.old_fattr = nfs_alloc_fattr();
	res.new_fattr = nfs_alloc_fattr();
	if (res.old_fattr == NULL || res.new_fattr == NULL)
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
2770

2771
	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
Linus Torvalds's avatar
Linus Torvalds committed
2772 2773
	if (!status) {
		update_changeattr(old_dir, &res.old_cinfo);
2774
		nfs_post_op_update_inode(old_dir, res.old_fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2775
		update_changeattr(new_dir, &res.new_cinfo);
2776
		nfs_post_op_update_inode(new_dir, res.new_fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2777
	}
2778 2779 2780
out:
	nfs_free_fattr(res.new_fattr);
	nfs_free_fattr(res.old_fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799
	return status;
}

static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
		struct inode *new_dir, struct qstr *new_name)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(old_dir),
				_nfs4_proc_rename(old_dir, old_name,
					new_dir, new_name),
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
2800
	struct nfs_server *server = NFS_SERVER(inode);
Linus Torvalds's avatar
Linus Torvalds committed
2801 2802 2803 2804
	struct nfs4_link_arg arg = {
		.fh     = NFS_FH(inode),
		.dir_fh = NFS_FH(dir),
		.name   = name,
2805 2806 2807 2808
		.bitmask = server->attr_bitmask,
	};
	struct nfs4_link_res res = {
		.server = server,
Linus Torvalds's avatar
Linus Torvalds committed
2809 2810 2811 2812
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
		.rpc_argp = &arg,
2813
		.rpc_resp = &res,
Linus Torvalds's avatar
Linus Torvalds committed
2814
	};
2815 2816 2817 2818 2819 2820
	int status = -ENOMEM;

	res.fattr = nfs_alloc_fattr();
	res.dir_attr = nfs_alloc_fattr();
	if (res.fattr == NULL || res.dir_attr == NULL)
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
2821

2822
	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
2823 2824 2825
	if (!status) {
		update_changeattr(dir, &res.cinfo);
		nfs_post_op_update_inode(dir, res.dir_attr);
2826
		nfs_post_op_update_inode(inode, res.fattr);
2827
	}
2828 2829 2830
out:
	nfs_free_fattr(res.dir_attr);
	nfs_free_fattr(res.fattr);
Linus Torvalds's avatar
Linus Torvalds committed
2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845
	return status;
}

static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(inode),
				_nfs4_proc_link(inode, dir, name),
				&exception);
	} while (exception.retry);
	return err;
}

2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884
struct nfs4_createdata {
	struct rpc_message msg;
	struct nfs4_create_arg arg;
	struct nfs4_create_res res;
	struct nfs_fh fh;
	struct nfs_fattr fattr;
	struct nfs_fattr dir_fattr;
};

static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
		struct qstr *name, struct iattr *sattr, u32 ftype)
{
	struct nfs4_createdata *data;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (data != NULL) {
		struct nfs_server *server = NFS_SERVER(dir);

		data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE];
		data->msg.rpc_argp = &data->arg;
		data->msg.rpc_resp = &data->res;
		data->arg.dir_fh = NFS_FH(dir);
		data->arg.server = server;
		data->arg.name = name;
		data->arg.attrs = sattr;
		data->arg.ftype = ftype;
		data->arg.bitmask = server->attr_bitmask;
		data->res.server = server;
		data->res.fh = &data->fh;
		data->res.fattr = &data->fattr;
		data->res.dir_fattr = &data->dir_fattr;
		nfs_fattr_init(data->res.fattr);
		nfs_fattr_init(data->res.dir_fattr);
	}
	return data;
}

static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
{
2885
	int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
2886
				    &data->arg.seq_args, &data->res.seq_res, 1);
2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899
	if (status == 0) {
		update_changeattr(dir, &data->res.dir_cinfo);
		nfs_post_op_update_inode(dir, data->res.dir_fattr);
		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
	}
	return status;
}

static void nfs4_free_createdata(struct nfs4_createdata *data)
{
	kfree(data);
}

2900
static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
2901
		struct page *page, unsigned int len, struct iattr *sattr)
Linus Torvalds's avatar
Linus Torvalds committed
2902
{
2903 2904
	struct nfs4_createdata *data;
	int status = -ENAMETOOLONG;
Linus Torvalds's avatar
Linus Torvalds committed
2905

2906
	if (len > NFS4_MAXPATHLEN)
2907
		goto out;
2908

2909 2910 2911 2912 2913 2914 2915 2916
	status = -ENOMEM;
	data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4LNK);
	if (data == NULL)
		goto out;

	data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK];
	data->arg.u.symlink.pages = &page;
	data->arg.u.symlink.len = len;
Linus Torvalds's avatar
Linus Torvalds committed
2917
	
2918 2919 2920 2921
	status = nfs4_do_create(dir, dentry, data);

	nfs4_free_createdata(data);
out:
Linus Torvalds's avatar
Linus Torvalds committed
2922 2923 2924
	return status;
}

2925
static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
2926
		struct page *page, unsigned int len, struct iattr *sattr)
Linus Torvalds's avatar
Linus Torvalds committed
2927 2928 2929 2930 2931
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(dir),
2932 2933
				_nfs4_proc_symlink(dir, dentry, page,
							len, sattr),
Linus Torvalds's avatar
Linus Torvalds committed
2934 2935 2936 2937 2938 2939 2940 2941
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr)
{
2942 2943
	struct nfs4_createdata *data;
	int status = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
2944

2945 2946 2947 2948 2949 2950 2951 2952
	data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4DIR);
	if (data == NULL)
		goto out;

	status = nfs4_do_create(dir, dentry, data);

	nfs4_free_createdata(data);
out:
Linus Torvalds's avatar
Linus Torvalds committed
2953 2954 2955 2956 2957 2958 2959 2960
	return status;
}

static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr)
{
	struct nfs4_exception exception = { };
	int err;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
2961 2962

	sattr->ia_mode &= ~current_umask();
Linus Torvalds's avatar
Linus Torvalds committed
2963 2964 2965 2966 2967 2968 2969 2970 2971
	do {
		err = nfs4_handle_exception(NFS_SERVER(dir),
				_nfs4_proc_mkdir(dir, dentry, sattr),
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
2972
		u64 cookie, struct page **pages, unsigned int count, int plus)
Linus Torvalds's avatar
Linus Torvalds committed
2973 2974 2975 2976
{
	struct inode		*dir = dentry->d_inode;
	struct nfs4_readdir_arg args = {
		.fh = NFS_FH(dir),
2977
		.pages = pages,
Linus Torvalds's avatar
Linus Torvalds committed
2978 2979
		.pgbase = 0,
		.count = count,
2980
		.bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask,
Bryan Schumaker's avatar
Bryan Schumaker committed
2981
		.plus = plus,
Linus Torvalds's avatar
Linus Torvalds committed
2982 2983 2984 2985 2986 2987 2988 2989 2990 2991
	};
	struct nfs4_readdir_res res;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR],
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_cred = cred,
	};
	int			status;

2992
	dprintk("%s: dentry = %s/%s, cookie = %Lu\n", __func__,
2993 2994 2995
			dentry->d_parent->d_name.name,
			dentry->d_name.name,
			(unsigned long long)cookie);
Linus Torvalds's avatar
Linus Torvalds committed
2996 2997
	nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
	res.pgbase = args.pgbase;
2998
	status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
2999
	if (status >= 0) {
Linus Torvalds's avatar
Linus Torvalds committed
3000
		memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
3001 3002
		status += args.pgbase;
	}
3003 3004 3005

	nfs_invalidate_atime(dir);

3006
	dprintk("%s: returns %d\n", __func__, status);
Linus Torvalds's avatar
Linus Torvalds committed
3007 3008 3009 3010
	return status;
}

static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
3011
		u64 cookie, struct page **pages, unsigned int count, int plus)
Linus Torvalds's avatar
Linus Torvalds committed
3012 3013 3014 3015 3016 3017
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
				_nfs4_proc_readdir(dentry, cred, cookie,
3018
					pages, count, plus),
Linus Torvalds's avatar
Linus Torvalds committed
3019 3020 3021 3022 3023 3024 3025 3026
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr, dev_t rdev)
{
3027 3028 3029
	struct nfs4_createdata *data;
	int mode = sattr->ia_mode;
	int status = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
3030 3031 3032

	BUG_ON(!(sattr->ia_valid & ATTR_MODE));
	BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
3033 3034 3035 3036 3037

	data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK);
	if (data == NULL)
		goto out;

Linus Torvalds's avatar
Linus Torvalds committed
3038
	if (S_ISFIFO(mode))
3039
		data->arg.ftype = NF4FIFO;
Linus Torvalds's avatar
Linus Torvalds committed
3040
	else if (S_ISBLK(mode)) {
3041 3042 3043
		data->arg.ftype = NF4BLK;
		data->arg.u.device.specdata1 = MAJOR(rdev);
		data->arg.u.device.specdata2 = MINOR(rdev);
Linus Torvalds's avatar
Linus Torvalds committed
3044 3045
	}
	else if (S_ISCHR(mode)) {
3046 3047 3048
		data->arg.ftype = NF4CHR;
		data->arg.u.device.specdata1 = MAJOR(rdev);
		data->arg.u.device.specdata2 = MINOR(rdev);
Linus Torvalds's avatar
Linus Torvalds committed
3049 3050
	}
	
3051 3052 3053 3054
	status = nfs4_do_create(dir, dentry, data);

	nfs4_free_createdata(data);
out:
Linus Torvalds's avatar
Linus Torvalds committed
3055 3056 3057 3058 3059 3060 3061 3062
	return status;
}

static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr, dev_t rdev)
{
	struct nfs4_exception exception = { };
	int err;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
3063 3064

	sattr->ia_mode &= ~current_umask();
Linus Torvalds's avatar
Linus Torvalds committed
3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079
	do {
		err = nfs4_handle_exception(NFS_SERVER(dir),
				_nfs4_proc_mknod(dir, dentry, sattr, rdev),
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
		 struct nfs_fsstat *fsstat)
{
	struct nfs4_statfs_arg args = {
		.fh = fhandle,
		.bitmask = server->attr_bitmask,
	};
Benny Halevy's avatar
Benny Halevy committed
3080 3081 3082
	struct nfs4_statfs_res res = {
		.fsstat = fsstat,
	};
Linus Torvalds's avatar
Linus Torvalds committed
3083 3084 3085
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS],
		.rpc_argp = &args,
Benny Halevy's avatar
Benny Halevy committed
3086
		.rpc_resp = &res,
Linus Torvalds's avatar
Linus Torvalds committed
3087 3088
	};

3089
	nfs_fattr_init(fsstat->fattr);
3090
	return  nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111
}

static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(server,
				_nfs4_proc_statfs(server, fhandle, fsstat),
				&exception);
	} while (exception.retry);
	return err;
}

static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
		struct nfs_fsinfo *fsinfo)
{
	struct nfs4_fsinfo_arg args = {
		.fh = fhandle,
		.bitmask = server->attr_bitmask,
	};
Benny Halevy's avatar
Benny Halevy committed
3112 3113 3114
	struct nfs4_fsinfo_res res = {
		.fsinfo = fsinfo,
	};
Linus Torvalds's avatar
Linus Torvalds committed
3115 3116 3117
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO],
		.rpc_argp = &args,
Benny Halevy's avatar
Benny Halevy committed
3118
		.rpc_resp = &res,
Linus Torvalds's avatar
Linus Torvalds committed
3119 3120
	};

3121
	return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138
}

static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
	struct nfs4_exception exception = { };
	int err;

	do {
		err = nfs4_handle_exception(server,
				_nfs4_do_fsinfo(server, fhandle, fsinfo),
				&exception);
	} while (exception.retry);
	return err;
}

static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
3139
	nfs_fattr_init(fsinfo->fattr);
Linus Torvalds's avatar
Linus Torvalds committed
3140 3141 3142 3143 3144 3145 3146 3147 3148 3149
	return nfs4_do_fsinfo(server, fhandle, fsinfo);
}

static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
		struct nfs_pathconf *pathconf)
{
	struct nfs4_pathconf_arg args = {
		.fh = fhandle,
		.bitmask = server->attr_bitmask,
	};
Benny Halevy's avatar
Benny Halevy committed
3150 3151 3152
	struct nfs4_pathconf_res res = {
		.pathconf = pathconf,
	};
Linus Torvalds's avatar
Linus Torvalds committed
3153 3154 3155
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
		.rpc_argp = &args,
Benny Halevy's avatar
Benny Halevy committed
3156
		.rpc_resp = &res,
Linus Torvalds's avatar
Linus Torvalds committed
3157 3158 3159 3160 3161 3162 3163 3164
	};

	/* None of the pathconf attributes are mandatory to implement */
	if ((args.bitmask[0] & nfs4_pathconf_bitmap[0]) == 0) {
		memset(pathconf, 0, sizeof(*pathconf));
		return 0;
	}

3165
	nfs_fattr_init(pathconf->fattr);
3166
	return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
Linus Torvalds's avatar
Linus Torvalds committed
3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182
}

static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
		struct nfs_pathconf *pathconf)
{
	struct nfs4_exception exception = { };
	int err;

	do {
		err = nfs4_handle_exception(server,
				_nfs4_proc_pathconf(server, fhandle, pathconf),
				&exception);
	} while (exception.retry);
	return err;
}

3183 3184 3185 3186 3187
void __nfs4_read_done_cb(struct nfs_read_data *data)
{
	nfs_invalidate_atime(data->inode);
}

3188
static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
Linus Torvalds's avatar
Linus Torvalds committed
3189
{
3190
	struct nfs_server *server = NFS_SERVER(data->inode);
Linus Torvalds's avatar
Linus Torvalds committed
3191

3192
	if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
3193
		rpc_restart_call_prepare(task);
3194
		return -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
3195
	}
3196

3197
	__nfs4_read_done_cb(data);
Linus Torvalds's avatar
Linus Torvalds committed
3198
	if (task->tk_status > 0)
3199 3200
		renew_lease(server, data->timestamp);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
3201 3202
}

3203 3204 3205 3206 3207 3208 3209 3210
static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
{

	dprintk("--> %s\n", __func__);

	if (!nfs4_sequence_done(task, &data->res.seq_res))
		return -EAGAIN;

3211 3212
	return data->read_done_cb ? data->read_done_cb(task, data) :
				    nfs4_read_done_cb(task, data);
3213 3214
}

3215
static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg)
Linus Torvalds's avatar
Linus Torvalds committed
3216 3217
{
	data->timestamp   = jiffies;
3218
	data->read_done_cb = nfs4_read_done_cb;
3219
	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
Linus Torvalds's avatar
Linus Torvalds committed
3220 3221
}

3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237
/* Reset the the nfs_read_data to send the read to the MDS. */
void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data)
{
	dprintk("%s Reset task for i/o through\n", __func__);
	put_lseg(data->lseg);
	data->lseg = NULL;
	/* offsets will differ in the dense stripe case */
	data->args.offset = data->mds_offset;
	data->ds_clp = NULL;
	data->args.fh     = NFS_FH(data->inode);
	data->read_done_cb = nfs4_read_done_cb;
	task->tk_ops = data->mds_ops;
	rpc_task_reset_client(task, NFS_CLIENT(data->inode));
}
EXPORT_SYMBOL_GPL(nfs4_reset_read);

3238
static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data)
Linus Torvalds's avatar
Linus Torvalds committed
3239 3240 3241
{
	struct inode *inode = data->inode;
	
3242
	if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
3243
		rpc_restart_call_prepare(task);
3244
		return -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
3245
	}
3246
	if (task->tk_status >= 0) {
Linus Torvalds's avatar
Linus Torvalds committed
3247
		renew_lease(NFS_SERVER(inode), data->timestamp);
3248
		nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
3249
	}
3250
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
3251 3252
}

3253 3254 3255 3256
static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
{
	if (!nfs4_sequence_done(task, &data->res.seq_res))
		return -EAGAIN;
3257 3258
	return data->write_done_cb ? data->write_done_cb(task, data) :
		nfs4_write_done_cb(task, data);
3259 3260
}

3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277
/* Reset the the nfs_write_data to send the write to the MDS. */
void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data)
{
	dprintk("%s Reset task for i/o through\n", __func__);
	put_lseg(data->lseg);
	data->lseg          = NULL;
	data->ds_clp        = NULL;
	data->write_done_cb = nfs4_write_done_cb;
	data->args.fh       = NFS_FH(data->inode);
	data->args.bitmask  = data->res.server->cache_consistency_bitmask;
	data->args.offset   = data->mds_offset;
	data->res.fattr     = &data->fattr;
	task->tk_ops        = data->mds_ops;
	rpc_task_reset_client(task, NFS_CLIENT(data->inode));
}
EXPORT_SYMBOL_GPL(nfs4_reset_write);

3278
static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg)
Linus Torvalds's avatar
Linus Torvalds committed
3279
{
3280 3281
	struct nfs_server *server = NFS_SERVER(data->inode);

3282 3283 3284 3285 3286
	if (data->lseg) {
		data->args.bitmask = NULL;
		data->res.fattr = NULL;
	} else
		data->args.bitmask = server->cache_consistency_bitmask;
3287 3288
	if (!data->write_done_cb)
		data->write_done_cb = nfs4_write_done_cb;
3289
	data->res.server = server;
Linus Torvalds's avatar
Linus Torvalds committed
3290 3291
	data->timestamp   = jiffies;

3292
	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
Linus Torvalds's avatar
Linus Torvalds committed
3293 3294
}

3295
static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data)
Linus Torvalds's avatar
Linus Torvalds committed
3296 3297
{
	struct inode *inode = data->inode;
3298

3299
	if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
3300
		rpc_restart_call_prepare(task);
3301
		return -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
3302
	}
3303
	nfs_refresh_inode(inode, data->res.fattr);
3304
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
3305 3306
}

3307 3308 3309 3310 3311 3312 3313
static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
{
	if (!nfs4_sequence_done(task, &data->res.seq_res))
		return -EAGAIN;
	return data->write_done_cb(task, data);
}

3314
static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
Linus Torvalds's avatar
Linus Torvalds committed
3315
{
3316
	struct nfs_server *server = NFS_SERVER(data->inode);
3317 3318 3319 3320 3321 3322

	if (data->lseg) {
		data->args.bitmask = NULL;
		data->res.fattr = NULL;
	} else
		data->args.bitmask = server->cache_consistency_bitmask;
3323 3324
	if (!data->write_done_cb)
		data->write_done_cb = nfs4_commit_done_cb;
3325
	data->res.server = server;
3326
	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
Linus Torvalds's avatar
Linus Torvalds committed
3327 3328
}

3329 3330 3331 3332 3333
struct nfs4_renewdata {
	struct nfs_client	*client;
	unsigned long		timestamp;
};

Linus Torvalds's avatar
Linus Torvalds committed
3334 3335 3336 3337
/*
 * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special
 * standalone procedure for queueing an asynchronous RENEW.
 */
3338
static void nfs4_renew_release(void *calldata)
3339
{
3340 3341
	struct nfs4_renewdata *data = calldata;
	struct nfs_client *clp = data->client;
3342

3343 3344 3345
	if (atomic_read(&clp->cl_count) > 1)
		nfs4_schedule_state_renewal(clp);
	nfs_put_client(clp);
3346
	kfree(data);
3347 3348
}

3349
static void nfs4_renew_done(struct rpc_task *task, void *calldata)
Linus Torvalds's avatar
Linus Torvalds committed
3350
{
3351 3352 3353
	struct nfs4_renewdata *data = calldata;
	struct nfs_client *clp = data->client;
	unsigned long timestamp = data->timestamp;
Linus Torvalds's avatar
Linus Torvalds committed
3354 3355

	if (task->tk_status < 0) {
3356
		/* Unless we're shutting down, schedule state recovery! */
3357 3358 3359
		if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0)
			return;
		if (task->tk_status != NFS4ERR_CB_PATH_DOWN) {
3360
			nfs4_schedule_lease_recovery(clp);
3361 3362 3363
			return;
		}
		nfs4_schedule_path_down_recovery(clp);
Linus Torvalds's avatar
Linus Torvalds committed
3364
	}
3365
	do_renew_lease(clp, timestamp);
Linus Torvalds's avatar
Linus Torvalds committed
3366 3367
}

3368 3369
static const struct rpc_call_ops nfs4_renew_ops = {
	.rpc_call_done = nfs4_renew_done,
3370
	.rpc_release = nfs4_renew_release,
3371 3372
};

3373
static int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags)
Linus Torvalds's avatar
Linus Torvalds committed
3374 3375 3376 3377
{
	struct rpc_message msg = {
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_RENEW],
		.rpc_argp	= clp,
3378
		.rpc_cred	= cred,
Linus Torvalds's avatar
Linus Torvalds committed
3379
	};
3380
	struct nfs4_renewdata *data;
Linus Torvalds's avatar
Linus Torvalds committed
3381

3382 3383
	if (renew_flags == 0)
		return 0;
3384 3385
	if (!atomic_inc_not_zero(&clp->cl_count))
		return -EIO;
3386
	data = kmalloc(sizeof(*data), GFP_NOFS);
3387 3388 3389 3390
	if (data == NULL)
		return -ENOMEM;
	data->client = clp;
	data->timestamp = jiffies;
Linus Torvalds's avatar
Linus Torvalds committed
3391
	return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
3392
			&nfs4_renew_ops, data);
Linus Torvalds's avatar
Linus Torvalds committed
3393 3394
}

3395
static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
Linus Torvalds's avatar
Linus Torvalds committed
3396 3397 3398 3399
{
	struct rpc_message msg = {
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_RENEW],
		.rpc_argp	= clp,
3400
		.rpc_cred	= cred,
Linus Torvalds's avatar
Linus Torvalds committed
3401 3402 3403 3404 3405 3406 3407
	};
	unsigned long now = jiffies;
	int status;

	status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
	if (status < 0)
		return status;
3408
	do_renew_lease(clp, now);
Linus Torvalds's avatar
Linus Torvalds committed
3409 3410 3411
	return 0;
}

3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437
static inline int nfs4_server_supports_acls(struct nfs_server *server)
{
	return (server->caps & NFS_CAP_ACLS)
		&& (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
		&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
}

/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that
 * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on
 * the stack.
 */
#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)

static void buf_to_pages(const void *buf, size_t buflen,
		struct page **pages, unsigned int *pgbase)
{
	const void *p = buf;

	*pgbase = offset_in_page(buf);
	p -= *pgbase;
	while (p < buf + buflen) {
		*(pages++) = virt_to_page(p);
		p += PAGE_CACHE_SIZE;
	}
}

3438 3439 3440 3441 3442 3443 3444 3445 3446
static int buf_to_pages_noslab(const void *buf, size_t buflen,
		struct page **pages, unsigned int *pgbase)
{
	struct page *newpage, **spages;
	int rc = 0;
	size_t len;
	spages = pages;

	do {
Jovi Zhang's avatar
Jovi Zhang committed
3447
		len = min_t(size_t, PAGE_CACHE_SIZE, buflen);
3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466
		newpage = alloc_page(GFP_KERNEL);

		if (newpage == NULL)
			goto unwind;
		memcpy(page_address(newpage), buf, len);
                buf += len;
                buflen -= len;
		*pages++ = newpage;
		rc++;
	} while (buflen != 0);

	return rc;

unwind:
	for(; rc > 0; rc--)
		__free_page(spages[rc-1]);
	return -ENOMEM;
}

3467 3468 3469
struct nfs4_cached_acl {
	int cached;
	size_t len;
Andrew Morton's avatar
Andrew Morton committed
3470
	char data[0];
3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533
};

static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl)
{
	struct nfs_inode *nfsi = NFS_I(inode);

	spin_lock(&inode->i_lock);
	kfree(nfsi->nfs4_acl);
	nfsi->nfs4_acl = acl;
	spin_unlock(&inode->i_lock);
}

static void nfs4_zap_acl_attr(struct inode *inode)
{
	nfs4_set_cached_acl(inode, NULL);
}

static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs4_cached_acl *acl;
	int ret = -ENOENT;

	spin_lock(&inode->i_lock);
	acl = nfsi->nfs4_acl;
	if (acl == NULL)
		goto out;
	if (buf == NULL) /* user is just asking for length */
		goto out_len;
	if (acl->cached == 0)
		goto out;
	ret = -ERANGE; /* see getxattr(2) man page */
	if (acl->len > buflen)
		goto out;
	memcpy(buf, acl->data, acl->len);
out_len:
	ret = acl->len;
out:
	spin_unlock(&inode->i_lock);
	return ret;
}

static void nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len)
{
	struct nfs4_cached_acl *acl;

	if (buf && acl_len <= PAGE_SIZE) {
		acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
		if (acl == NULL)
			goto out;
		acl->cached = 1;
		memcpy(acl->data, buf, acl_len);
	} else {
		acl = kmalloc(sizeof(*acl), GFP_KERNEL);
		if (acl == NULL)
			goto out;
		acl->cached = 0;
	}
	acl->len = acl_len;
out:
	nfs4_set_cached_acl(inode, acl);
}

3534
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
3535 3536 3537 3538 3539 3540 3541
{
	struct page *pages[NFS4ACL_MAXPAGES];
	struct nfs_getaclargs args = {
		.fh = NFS_FH(inode),
		.acl_pages = pages,
		.acl_len = buflen,
	};
Benny Halevy's avatar
Benny Halevy committed
3542 3543 3544
	struct nfs_getaclres res = {
		.acl_len = buflen,
	};
3545
	void *resp_buf;
3546 3547 3548
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
		.rpc_argp = &args,
Benny Halevy's avatar
Benny Halevy committed
3549
		.rpc_resp = &res,
3550
	};
3551
	struct page *localpage = NULL;
3552 3553
	int ret;

3554 3555 3556 3557 3558 3559 3560 3561 3562
	if (buflen < PAGE_SIZE) {
		/* As long as we're doing a round trip to the server anyway,
		 * let's be prepared for a page of acl data. */
		localpage = alloc_page(GFP_KERNEL);
		resp_buf = page_address(localpage);
		if (localpage == NULL)
			return -ENOMEM;
		args.acl_pages[0] = localpage;
		args.acl_pgbase = 0;
Benny Halevy's avatar
Benny Halevy committed
3563
		args.acl_len = PAGE_SIZE;
3564 3565 3566 3567
	} else {
		resp_buf = buf;
		buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
	}
3568
	ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
3569 3570
	if (ret)
		goto out_free;
Benny Halevy's avatar
Benny Halevy committed
3571 3572
	if (res.acl_len > args.acl_len)
		nfs4_write_cached_acl(inode, NULL, res.acl_len);
3573
	else
Benny Halevy's avatar
Benny Halevy committed
3574
		nfs4_write_cached_acl(inode, resp_buf, res.acl_len);
3575 3576
	if (buf) {
		ret = -ERANGE;
Benny Halevy's avatar
Benny Halevy committed
3577
		if (res.acl_len > buflen)
3578 3579
			goto out_free;
		if (localpage)
Benny Halevy's avatar
Benny Halevy committed
3580
			memcpy(buf, resp_buf, res.acl_len);
3581
	}
Benny Halevy's avatar
Benny Halevy committed
3582
	ret = res.acl_len;
3583 3584 3585
out_free:
	if (localpage)
		__free_page(localpage);
3586 3587 3588
	return ret;
}

3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601
static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
	struct nfs4_exception exception = { };
	ssize_t ret;
	do {
		ret = __nfs4_get_acl_uncached(inode, buf, buflen);
		if (ret >= 0)
			break;
		ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception);
	} while (exception.retry);
	return ret;
}

3602 3603 3604 3605 3606 3607 3608 3609 3610 3611
static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
{
	struct nfs_server *server = NFS_SERVER(inode);
	int ret;

	if (!nfs4_server_supports_acls(server))
		return -EOPNOTSUPP;
	ret = nfs_revalidate_inode(server, inode);
	if (ret < 0)
		return ret;
3612 3613
	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
		nfs_zap_acl_cache(inode);
3614 3615 3616 3617 3618 3619
	ret = nfs4_read_cached_acl(inode, buf, buflen);
	if (ret != -ENOENT)
		return ret;
	return nfs4_get_acl_uncached(inode, buf, buflen);
}

3620
static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
3621 3622 3623 3624 3625 3626 3627 3628
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct page *pages[NFS4ACL_MAXPAGES];
	struct nfs_setaclargs arg = {
		.fh		= NFS_FH(inode),
		.acl_pages	= pages,
		.acl_len	= buflen,
	};
Benny Halevy's avatar
Benny Halevy committed
3629
	struct nfs_setaclres res;
3630 3631 3632
	struct rpc_message msg = {
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_SETACL],
		.rpc_argp	= &arg,
Benny Halevy's avatar
Benny Halevy committed
3633
		.rpc_resp	= &res,
3634
	};
3635
	int ret, i;
3636 3637 3638

	if (!nfs4_server_supports_acls(server))
		return -EOPNOTSUPP;
3639 3640 3641
	i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
	if (i < 0)
		return i;
3642
	nfs_inode_return_delegation(inode);
3643
	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
3644 3645 3646 3647 3648 3649 3650 3651

	/*
	 * Free each page after tx, so the only ref left is
	 * held by the network stack
	 */
	for (; i > 0; i--)
		put_page(pages[i-1]);

3652 3653 3654 3655 3656 3657 3658
	/*
	 * Acl update can result in inode attribute update.
	 * so mark the attribute cache invalid.
	 */
	spin_lock(&inode->i_lock);
	NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
	spin_unlock(&inode->i_lock);
3659 3660
	nfs_access_zap_cache(inode);
	nfs_zap_acl_cache(inode);
3661 3662 3663
	return ret;
}

3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675
static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(inode),
				__nfs4_proc_set_acl(inode, buf, buflen),
				&exception);
	} while (exception.retry);
	return err;
}

Linus Torvalds's avatar
Linus Torvalds committed
3676
static int
3677
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
Linus Torvalds's avatar
Linus Torvalds committed
3678
{
3679 3680 3681
	struct nfs_client *clp = server->nfs_client;

	if (task->tk_status >= 0)
Linus Torvalds's avatar
Linus Torvalds committed
3682 3683
		return 0;
	switch(task->tk_status) {
3684 3685 3686 3687 3688
		case -NFS4ERR_ADMIN_REVOKED:
		case -NFS4ERR_BAD_STATEID:
		case -NFS4ERR_OPENMODE:
			if (state == NULL)
				break;
3689 3690
			nfs4_schedule_stateid_recovery(server, state);
			goto wait_on_recovery;
3691 3692 3693
		case -NFS4ERR_EXPIRED:
			if (state != NULL)
				nfs4_schedule_stateid_recovery(server, state);
Linus Torvalds's avatar
Linus Torvalds committed
3694
		case -NFS4ERR_STALE_STATEID:
3695
		case -NFS4ERR_STALE_CLIENTID:
3696 3697
			nfs4_schedule_lease_recovery(clp);
			goto wait_on_recovery;
3698 3699 3700 3701 3702 3703 3704 3705 3706 3707
#if defined(CONFIG_NFS_V4_1)
		case -NFS4ERR_BADSESSION:
		case -NFS4ERR_BADSLOT:
		case -NFS4ERR_BAD_HIGH_SLOT:
		case -NFS4ERR_DEADSESSION:
		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
		case -NFS4ERR_SEQ_FALSE_RETRY:
		case -NFS4ERR_SEQ_MISORDERED:
			dprintk("%s ERROR %d, Reset session\n", __func__,
				task->tk_status);
3708
			nfs4_schedule_session_recovery(clp->cl_session);
3709 3710 3711
			task->tk_status = 0;
			return -EAGAIN;
#endif /* CONFIG_NFS_V4_1 */
Linus Torvalds's avatar
Linus Torvalds committed
3712
		case -NFS4ERR_DELAY:
3713
			nfs_inc_server_stats(server, NFSIOS_DELAY);
3714
		case -NFS4ERR_GRACE:
3715
		case -EKEYEXPIRED:
Linus Torvalds's avatar
Linus Torvalds committed
3716 3717 3718
			rpc_delay(task, NFS4_POLL_RETRY_MAX);
			task->tk_status = 0;
			return -EAGAIN;
3719
		case -NFS4ERR_RETRY_UNCACHED_REP:
Linus Torvalds's avatar
Linus Torvalds committed
3720 3721 3722 3723 3724 3725
		case -NFS4ERR_OLD_STATEID:
			task->tk_status = 0;
			return -EAGAIN;
	}
	task->tk_status = nfs4_map_errors(task->tk_status);
	return 0;
3726
wait_on_recovery:
3727 3728 3729 3730 3731
	rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
	if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
		rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
	task->tk_status = 0;
	return -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
3732 3733
}

3734 3735 3736
int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
		unsigned short port, struct rpc_cred *cred,
		struct nfs4_setclientid_res *res)
Linus Torvalds's avatar
Linus Torvalds committed
3737 3738 3739 3740 3741
{
	nfs4_verifier sc_verifier;
	struct nfs4_setclientid setclientid = {
		.sc_verifier = &sc_verifier,
		.sc_prog = program,
3742
		.sc_cb_ident = clp->cl_cb_ident,
Linus Torvalds's avatar
Linus Torvalds committed
3743 3744 3745 3746
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID],
		.rpc_argp = &setclientid,
3747
		.rpc_resp = res,
3748
		.rpc_cred = cred,
Linus Torvalds's avatar
Linus Torvalds committed
3749
	};
3750
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed
3751 3752 3753
	int loop = 0;
	int status;

3754
	p = (__be32*)sc_verifier.data;
Linus Torvalds's avatar
Linus Torvalds committed
3755 3756 3757 3758 3759
	*p++ = htonl((u32)clp->cl_boot_time.tv_sec);
	*p = htonl((u32)clp->cl_boot_time.tv_nsec);

	for(;;) {
		setclientid.sc_name_len = scnprintf(setclientid.sc_name,
3760
				sizeof(setclientid.sc_name), "%s/%s %s %s %u",
3761 3762 3763
				clp->cl_ipaddr,
				rpc_peeraddr2str(clp->cl_rpcclient,
							RPC_DISPLAY_ADDR),
3764 3765
				rpc_peeraddr2str(clp->cl_rpcclient,
							RPC_DISPLAY_PROTO),
3766
				clp->cl_rpcclient->cl_auth->au_ops->au_name,
Linus Torvalds's avatar
Linus Torvalds committed
3767 3768
				clp->cl_id_uniquifier);
		setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
3769 3770 3771
				sizeof(setclientid.sc_netid),
				rpc_peeraddr2str(clp->cl_rpcclient,
							RPC_DISPLAY_NETID));
Linus Torvalds's avatar
Linus Torvalds committed
3772
		setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
3773
				sizeof(setclientid.sc_uaddr), "%s.%u.%u",
Linus Torvalds's avatar
Linus Torvalds committed
3774 3775
				clp->cl_ipaddr, port >> 8, port & 255);

3776
		status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
3777 3778
		if (status != -NFS4ERR_CLID_INUSE)
			break;
3779 3780
		if (loop != 0) {
			++clp->cl_id_uniquifier;
Linus Torvalds's avatar
Linus Torvalds committed
3781
			break;
3782 3783 3784
		}
		++loop;
		ssleep(clp->cl_lease_time / HZ + 1);
Linus Torvalds's avatar
Linus Torvalds committed
3785 3786 3787 3788
	}
	return status;
}

3789
int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
3790 3791
		struct nfs4_setclientid_res *arg,
		struct rpc_cred *cred)
Linus Torvalds's avatar
Linus Torvalds committed
3792 3793 3794 3795
{
	struct nfs_fsinfo fsinfo;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
3796
		.rpc_argp = arg,
Linus Torvalds's avatar
Linus Torvalds committed
3797
		.rpc_resp = &fsinfo,
3798
		.rpc_cred = cred,
Linus Torvalds's avatar
Linus Torvalds committed
3799 3800 3801 3802 3803
	};
	unsigned long now;
	int status;

	now = jiffies;
3804
	status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
3805 3806 3807 3808 3809 3810 3811 3812 3813
	if (status == 0) {
		spin_lock(&clp->cl_lock);
		clp->cl_lease_time = fsinfo.lease_time * HZ;
		clp->cl_last_renewal = now;
		spin_unlock(&clp->cl_lock);
	}
	return status;
}

3814 3815
struct nfs4_delegreturndata {
	struct nfs4_delegreturnargs args;
3816
	struct nfs4_delegreturnres res;
3817 3818
	struct nfs_fh fh;
	nfs4_stateid stateid;
3819
	unsigned long timestamp;
3820
	struct nfs_fattr fattr;
3821 3822 3823 3824 3825 3826
	int rpc_status;
};

static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_delegreturndata *data = calldata;
3827

3828 3829
	if (!nfs4_sequence_done(task, &data->res.seq_res))
		return;
3830

3831 3832 3833 3834
	switch (task->tk_status) {
	case -NFS4ERR_STALE_STATEID:
	case -NFS4ERR_EXPIRED:
	case 0:
3835
		renew_lease(data->res.server, data->timestamp);
3836 3837 3838 3839
		break;
	default:
		if (nfs4_async_handle_error(task, data->res.server, NULL) ==
				-EAGAIN) {
3840
			rpc_restart_call_prepare(task);
3841 3842 3843 3844
			return;
		}
	}
	data->rpc_status = task->tk_status;
3845 3846 3847 3848 3849 3850 3851
}

static void nfs4_delegreturn_release(void *calldata)
{
	kfree(calldata);
}

3852 3853 3854 3855 3856 3857 3858
#if defined(CONFIG_NFS_V4_1)
static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
{
	struct nfs4_delegreturndata *d_data;

	d_data = (struct nfs4_delegreturndata *)data;

3859
	if (nfs4_setup_sequence(d_data->res.server,
3860 3861 3862 3863 3864 3865 3866
				&d_data->args.seq_args,
				&d_data->res.seq_res, 1, task))
		return;
	rpc_call_start(task);
}
#endif /* CONFIG_NFS_V4_1 */

3867
static const struct rpc_call_ops nfs4_delegreturn_ops = {
3868 3869 3870
#if defined(CONFIG_NFS_V4_1)
	.rpc_call_prepare = nfs4_delegreturn_prepare,
#endif /* CONFIG_NFS_V4_1 */
3871 3872 3873 3874
	.rpc_call_done = nfs4_delegreturn_done,
	.rpc_release = nfs4_delegreturn_release,
};

3875
static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
3876 3877
{
	struct nfs4_delegreturndata *data;
3878
	struct nfs_server *server = NFS_SERVER(inode);
3879
	struct rpc_task *task;
3880 3881 3882 3883
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN],
		.rpc_cred = cred,
	};
3884 3885
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
3886
		.rpc_message = &msg,
3887 3888 3889
		.callback_ops = &nfs4_delegreturn_ops,
		.flags = RPC_TASK_ASYNC,
	};
3890
	int status = 0;
3891

3892
	data = kzalloc(sizeof(*data), GFP_NOFS);
3893 3894 3895 3896
	if (data == NULL)
		return -ENOMEM;
	data->args.fhandle = &data->fh;
	data->args.stateid = &data->stateid;
3897
	data->args.bitmask = server->attr_bitmask;
3898 3899
	nfs_copy_fh(&data->fh, NFS_FH(inode));
	memcpy(&data->stateid, stateid, sizeof(data->stateid));
3900 3901
	data->res.fattr = &data->fattr;
	data->res.server = server;
3902
	nfs_fattr_init(data->res.fattr);
3903
	data->timestamp = jiffies;
3904 3905
	data->rpc_status = 0;

3906
	task_setup_data.callback_data = data;
3907 3908
	msg.rpc_argp = &data->args;
	msg.rpc_resp = &data->res;
3909
	task = rpc_run_task(&task_setup_data);
3910
	if (IS_ERR(task))
3911
		return PTR_ERR(task);
3912 3913
	if (!issync)
		goto out;
3914
	status = nfs4_wait_for_completion_rpc_task(task);
3915 3916 3917 3918 3919 3920 3921
	if (status != 0)
		goto out;
	status = data->rpc_status;
	if (status != 0)
		goto out;
	nfs_refresh_inode(inode, &data->fattr);
out:
3922
	rpc_put_task(task);
3923
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
3924 3925
}

3926
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
Linus Torvalds's avatar
Linus Torvalds committed
3927 3928 3929 3930 3931
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs4_exception exception = { };
	int err;
	do {
3932
		err = _nfs4_proc_delegreturn(inode, cred, stateid, issync);
Linus Torvalds's avatar
Linus Torvalds committed
3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952
		switch (err) {
			case -NFS4ERR_STALE_STATEID:
			case -NFS4ERR_EXPIRED:
			case 0:
				return 0;
		}
		err = nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
	return err;
}

#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)

/* 
 * sleep, with exponential backoff, and retry the LOCK operation. 
 */
static unsigned long
nfs4_set_lock_task_retry(unsigned long timeout)
{
3953
	schedule_timeout_killable(timeout);
Linus Torvalds's avatar
Linus Torvalds committed
3954 3955 3956 3957 3958 3959 3960 3961 3962 3963
	timeout <<= 1;
	if (timeout > NFS4_LOCK_MAXTIMEOUT)
		return NFS4_LOCK_MAXTIMEOUT;
	return timeout;
}

static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
	struct inode *inode = state->inode;
	struct nfs_server *server = NFS_SERVER(inode);
3964
	struct nfs_client *clp = server->nfs_client;
Trond Myklebust's avatar
Trond Myklebust committed
3965
	struct nfs_lockt_args arg = {
Linus Torvalds's avatar
Linus Torvalds committed
3966
		.fh = NFS_FH(inode),
Trond Myklebust's avatar
Trond Myklebust committed
3967
		.fl = request,
Linus Torvalds's avatar
Linus Torvalds committed
3968
	};
Trond Myklebust's avatar
Trond Myklebust committed
3969 3970
	struct nfs_lockt_res res = {
		.denied = request,
Linus Torvalds's avatar
Linus Torvalds committed
3971 3972 3973 3974 3975 3976 3977 3978 3979 3980
	};
	struct rpc_message msg = {
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_LOCKT],
		.rpc_argp       = &arg,
		.rpc_resp       = &res,
		.rpc_cred	= state->owner->so_cred,
	};
	struct nfs4_lock_state *lsp;
	int status;

Trond Myklebust's avatar
Trond Myklebust committed
3981
	arg.lock_owner.clientid = clp->cl_clientid;
3982 3983 3984 3985
	status = nfs4_set_lock_state(state, request);
	if (status != 0)
		goto out;
	lsp = request->fl_u.nfs4_fl.owner;
3986
	arg.lock_owner.id = lsp->ls_id.id;
3987
	arg.lock_owner.s_dev = server->s_dev;
3988
	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
Trond Myklebust's avatar
Trond Myklebust committed
3989 3990 3991 3992 3993 3994
	switch (status) {
		case 0:
			request->fl_type = F_UNLCK;
			break;
		case -NFS4ERR_DENIED:
			status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
3995
	}
3996
	request->fl_ops->fl_release_private(request);
3997
out:
Linus Torvalds's avatar
Linus Torvalds committed
3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029
	return status;
}

static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
	struct nfs4_exception exception = { };
	int err;

	do {
		err = nfs4_handle_exception(NFS_SERVER(state->inode),
				_nfs4_proc_getlk(state, cmd, request),
				&exception);
	} while (exception.retry);
	return err;
}

static int do_vfs_lock(struct file *file, struct file_lock *fl)
{
	int res = 0;
	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
		case FL_POSIX:
			res = posix_lock_file_wait(file, fl);
			break;
		case FL_FLOCK:
			res = flock_lock_file_wait(file, fl);
			break;
		default:
			BUG();
	}
	return res;
}

4030
struct nfs4_unlockdata {
Trond Myklebust's avatar
Trond Myklebust committed
4031 4032
	struct nfs_locku_args arg;
	struct nfs_locku_res res;
4033 4034
	struct nfs4_lock_state *lsp;
	struct nfs_open_context *ctx;
Trond Myklebust's avatar
Trond Myklebust committed
4035 4036
	struct file_lock fl;
	const struct nfs_server *server;
4037
	unsigned long timestamp;
4038 4039
};

Trond Myklebust's avatar
Trond Myklebust committed
4040 4041 4042 4043 4044 4045 4046 4047
static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
		struct nfs_open_context *ctx,
		struct nfs4_lock_state *lsp,
		struct nfs_seqid *seqid)
{
	struct nfs4_unlockdata *p;
	struct inode *inode = lsp->ls_state->inode;

4048
	p = kzalloc(sizeof(*p), GFP_NOFS);
Trond Myklebust's avatar
Trond Myklebust committed
4049 4050 4051 4052 4053
	if (p == NULL)
		return NULL;
	p->arg.fh = NFS_FH(inode);
	p->arg.fl = &p->fl;
	p->arg.seqid = seqid;
4054
	p->res.seqid = seqid;
Trond Myklebust's avatar
Trond Myklebust committed
4055 4056 4057 4058 4059 4060 4061 4062 4063 4064
	p->arg.stateid = &lsp->ls_stateid;
	p->lsp = lsp;
	atomic_inc(&lsp->ls_count);
	/* Ensure we don't close file until we're done freeing locks! */
	p->ctx = get_nfs_open_context(ctx);
	memcpy(&p->fl, fl, sizeof(p->fl));
	p->server = NFS_SERVER(inode);
	return p;
}

4065
static void nfs4_locku_release_calldata(void *data)
4066
{
4067
	struct nfs4_unlockdata *calldata = data;
Trond Myklebust's avatar
Trond Myklebust committed
4068
	nfs_free_seqid(calldata->arg.seqid);
4069 4070 4071
	nfs4_put_lock_state(calldata->lsp);
	put_nfs_open_context(calldata->ctx);
	kfree(calldata);
4072 4073
}

4074
static void nfs4_locku_done(struct rpc_task *task, void *data)
4075
{
4076
	struct nfs4_unlockdata *calldata = data;
4077

4078 4079
	if (!nfs4_sequence_done(task, &calldata->res.seq_res))
		return;
4080 4081 4082
	switch (task->tk_status) {
		case 0:
			memcpy(calldata->lsp->ls_stateid.data,
Trond Myklebust's avatar
Trond Myklebust committed
4083
					calldata->res.stateid.data,
4084
					sizeof(calldata->lsp->ls_stateid.data));
4085
			renew_lease(calldata->server, calldata->timestamp);
4086
			break;
4087 4088
		case -NFS4ERR_BAD_STATEID:
		case -NFS4ERR_OLD_STATEID:
4089 4090 4091 4092
		case -NFS4ERR_STALE_STATEID:
		case -NFS4ERR_EXPIRED:
			break;
		default:
4093
			if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
4094
				rpc_restart_call_prepare(task);
4095 4096 4097
	}
}

Trond Myklebust's avatar
Trond Myklebust committed
4098
static void nfs4_locku_prepare(struct rpc_task *task, void *data)
4099
{
Trond Myklebust's avatar
Trond Myklebust committed
4100
	struct nfs4_unlockdata *calldata = data;
4101

Trond Myklebust's avatar
Trond Myklebust committed
4102
	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
4103 4104
		return;
	if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) {
4105 4106
		/* Note: exit _without_ running nfs4_locku_done */
		task->tk_action = NULL;
4107 4108
		return;
	}
4109
	calldata->timestamp = jiffies;
4110
	if (nfs4_setup_sequence(calldata->server,
4111 4112 4113
				&calldata->arg.seq_args,
				&calldata->res.seq_res, 1, task))
		return;
4114
	rpc_call_start(task);
4115 4116
}

4117
static const struct rpc_call_ops nfs4_locku_ops = {
Trond Myklebust's avatar
Trond Myklebust committed
4118
	.rpc_call_prepare = nfs4_locku_prepare,
4119
	.rpc_call_done = nfs4_locku_done,
4120
	.rpc_release = nfs4_locku_release_calldata,
4121 4122
};

4123 4124 4125 4126 4127 4128
static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
		struct nfs_open_context *ctx,
		struct nfs4_lock_state *lsp,
		struct nfs_seqid *seqid)
{
	struct nfs4_unlockdata *data;
4129 4130 4131 4132
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU],
		.rpc_cred = ctx->cred,
	};
4133 4134
	struct rpc_task_setup task_setup_data = {
		.rpc_client = NFS_CLIENT(lsp->ls_state->inode),
4135
		.rpc_message = &msg,
4136
		.callback_ops = &nfs4_locku_ops,
4137
		.workqueue = nfsiod_workqueue,
4138 4139
		.flags = RPC_TASK_ASYNC,
	};
4140

4141 4142 4143 4144 4145
	/* Ensure this is an unlock - when canceling a lock, the
	 * canceled lock is passed in, and it won't be an unlock.
	 */
	fl->fl_type = F_UNLCK;

4146 4147 4148 4149 4150 4151
	data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
	if (data == NULL) {
		nfs_free_seqid(seqid);
		return ERR_PTR(-ENOMEM);
	}

4152 4153
	msg.rpc_argp = &data->arg;
	msg.rpc_resp = &data->res;
4154 4155
	task_setup_data.callback_data = data;
	return rpc_run_task(&task_setup_data);
4156 4157
}

4158 4159
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
4160
	struct nfs_inode *nfsi = NFS_I(state->inode);
Trond Myklebust's avatar
Trond Myklebust committed
4161
	struct nfs_seqid *seqid;
Linus Torvalds's avatar
Linus Torvalds committed
4162
	struct nfs4_lock_state *lsp;
4163 4164
	struct rpc_task *task;
	int status = 0;
4165
	unsigned char fl_flags = request->fl_flags;
4166

4167
	status = nfs4_set_lock_state(state, request);
4168 4169
	/* Unlock _before_ we do the RPC call */
	request->fl_flags |= FL_EXISTS;
4170 4171 4172
	down_read(&nfsi->rwsem);
	if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
		up_read(&nfsi->rwsem);
4173
		goto out;
4174 4175
	}
	up_read(&nfsi->rwsem);
4176
	if (status != 0)
4177 4178 4179 4180
		goto out;
	/* Is this a delegated lock? */
	if (test_bit(NFS_DELEGATED_STATE, &state->flags))
		goto out;
4181
	lsp = request->fl_u.nfs4_fl.owner;
4182
	seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
4183
	status = -ENOMEM;
Trond Myklebust's avatar
Trond Myklebust committed
4184
	if (seqid == NULL)
4185
		goto out;
4186
	task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid);
4187 4188
	status = PTR_ERR(task);
	if (IS_ERR(task))
4189
		goto out;
4190
	status = nfs4_wait_for_completion_rpc_task(task);
4191
	rpc_put_task(task);
4192
out:
4193
	request->fl_flags = fl_flags;
Linus Torvalds's avatar
Linus Torvalds committed
4194 4195 4196
	return status;
}

4197 4198 4199 4200 4201 4202
struct nfs4_lockdata {
	struct nfs_lock_args arg;
	struct nfs_lock_res res;
	struct nfs4_lock_state *lsp;
	struct nfs_open_context *ctx;
	struct file_lock fl;
4203
	unsigned long timestamp;
4204 4205
	int rpc_status;
	int cancelled;
4206
	struct nfs_server *server;
4207 4208 4209
};

static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
4210 4211
		struct nfs_open_context *ctx, struct nfs4_lock_state *lsp,
		gfp_t gfp_mask)
Linus Torvalds's avatar
Linus Torvalds committed
4212
{
4213 4214
	struct nfs4_lockdata *p;
	struct inode *inode = lsp->ls_state->inode;
Linus Torvalds's avatar
Linus Torvalds committed
4215
	struct nfs_server *server = NFS_SERVER(inode);
4216

4217
	p = kzalloc(sizeof(*p), gfp_mask);
4218 4219 4220 4221 4222
	if (p == NULL)
		return NULL;

	p->arg.fh = NFS_FH(inode);
	p->arg.fl = &p->fl;
4223
	p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid, gfp_mask);
4224 4225
	if (p->arg.open_seqid == NULL)
		goto out_free;
4226
	p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid, gfp_mask);
4227
	if (p->arg.lock_seqid == NULL)
4228
		goto out_free_seqid;
4229
	p->arg.lock_stateid = &lsp->ls_stateid;
4230
	p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
4231
	p->arg.lock_owner.id = lsp->ls_id.id;
4232
	p->arg.lock_owner.s_dev = server->s_dev;
4233
	p->res.lock_seqid = p->arg.lock_seqid;
4234
	p->lsp = lsp;
4235
	p->server = server;
4236 4237 4238 4239
	atomic_inc(&lsp->ls_count);
	p->ctx = get_nfs_open_context(ctx);
	memcpy(&p->fl, fl, sizeof(p->fl));
	return p;
4240 4241
out_free_seqid:
	nfs_free_seqid(p->arg.open_seqid);
4242 4243 4244 4245 4246 4247 4248 4249 4250
out_free:
	kfree(p);
	return NULL;
}

static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_lockdata *data = calldata;
	struct nfs4_state *state = data->lsp->ls_state;
4251

4252
	dprintk("%s: begin!\n", __func__);
4253 4254
	if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0)
		return;
4255 4256
	/* Do we need to do an open_to_lock_owner? */
	if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
4257 4258
		if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0)
			return;
4259 4260
		data->arg.open_stateid = &state->stateid;
		data->arg.new_lock_owner = 1;
4261
		data->res.open_seqid = data->arg.open_seqid;
4262 4263
	} else
		data->arg.new_lock_owner = 0;
4264
	data->timestamp = jiffies;
4265 4266
	if (nfs4_setup_sequence(data->server,
				&data->arg.seq_args,
4267 4268
				&data->res.seq_res, 1, task))
		return;
4269
	rpc_call_start(task);
4270
	dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
4271 4272
}

4273 4274 4275 4276 4277 4278
static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
{
	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
	nfs4_lock_prepare(task, calldata);
}

4279 4280 4281 4282
static void nfs4_lock_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_lockdata *data = calldata;

4283
	dprintk("%s: begin!\n", __func__);
4284

4285 4286
	if (!nfs4_sequence_done(task, &data->res.seq_res))
		return;
4287

4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298
	data->rpc_status = task->tk_status;
	if (data->arg.new_lock_owner != 0) {
		if (data->rpc_status == 0)
			nfs_confirm_seqid(&data->lsp->ls_seqid, 0);
		else
			goto out;
	}
	if (data->rpc_status == 0) {
		memcpy(data->lsp->ls_stateid.data, data->res.stateid.data,
					sizeof(data->lsp->ls_stateid.data));
		data->lsp->ls_flags |= NFS_LOCK_INITIALIZED;
4299
		renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp);
4300 4301
	}
out:
4302
	dprintk("%s: done, ret = %d!\n", __func__, data->rpc_status);
4303 4304 4305 4306 4307 4308
}

static void nfs4_lock_release(void *calldata)
{
	struct nfs4_lockdata *data = calldata;

4309
	dprintk("%s: begin!\n", __func__);
4310
	nfs_free_seqid(data->arg.open_seqid);
4311 4312 4313 4314 4315
	if (data->cancelled != 0) {
		struct rpc_task *task;
		task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
				data->arg.lock_seqid);
		if (!IS_ERR(task))
4316
			rpc_put_task_async(task);
4317
		dprintk("%s: cancelling lock!\n", __func__);
4318 4319 4320 4321 4322
	} else
		nfs_free_seqid(data->arg.lock_seqid);
	nfs4_put_lock_state(data->lsp);
	put_nfs_open_context(data->ctx);
	kfree(data);
4323
	dprintk("%s: done!\n", __func__);
4324 4325 4326 4327 4328 4329 4330 4331
}

static const struct rpc_call_ops nfs4_lock_ops = {
	.rpc_call_prepare = nfs4_lock_prepare,
	.rpc_call_done = nfs4_lock_done,
	.rpc_release = nfs4_lock_release,
};

4332 4333 4334 4335 4336 4337
static const struct rpc_call_ops nfs4_recover_lock_ops = {
	.rpc_call_prepare = nfs4_recover_lock_prepare,
	.rpc_call_done = nfs4_lock_done,
	.rpc_release = nfs4_lock_release,
};

4338 4339 4340 4341 4342
static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error)
{
	switch (error) {
	case -NFS4ERR_ADMIN_REVOKED:
	case -NFS4ERR_BAD_STATEID:
4343
		lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
4344 4345
		if (new_lock_owner != 0 ||
		   (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0)
4346
			nfs4_schedule_stateid_recovery(server, lsp->ls_state);
4347 4348 4349
		break;
	case -NFS4ERR_STALE_STATEID:
		lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
4350 4351
	case -NFS4ERR_EXPIRED:
		nfs4_schedule_lease_recovery(server->nfs_client);
4352 4353 4354
	};
}

4355
static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
4356 4357 4358
{
	struct nfs4_lockdata *data;
	struct rpc_task *task;
4359 4360 4361 4362
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK],
		.rpc_cred = state->owner->so_cred,
	};
4363 4364
	struct rpc_task_setup task_setup_data = {
		.rpc_client = NFS_CLIENT(state->inode),
4365
		.rpc_message = &msg,
4366
		.callback_ops = &nfs4_lock_ops,
4367
		.workqueue = nfsiod_workqueue,
4368 4369
		.flags = RPC_TASK_ASYNC,
	};
4370 4371
	int ret;

4372
	dprintk("%s: begin!\n", __func__);
4373
	data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file),
4374 4375
			fl->fl_u.nfs4_fl.owner,
			recovery_type == NFS_LOCK_NEW ? GFP_KERNEL : GFP_NOFS);
4376 4377 4378 4379
	if (data == NULL)
		return -ENOMEM;
	if (IS_SETLKW(cmd))
		data->arg.block = 1;
4380 4381
	if (recovery_type > NFS_LOCK_NEW) {
		if (recovery_type == NFS_LOCK_RECLAIM)
4382
			data->arg.reclaim = NFS_LOCK_RECLAIM;
4383 4384
		task_setup_data.callback_ops = &nfs4_recover_lock_ops;
	}
4385 4386
	msg.rpc_argp = &data->arg;
	msg.rpc_resp = &data->res;
4387 4388
	task_setup_data.callback_data = data;
	task = rpc_run_task(&task_setup_data);
4389
	if (IS_ERR(task))
4390 4391 4392 4393
		return PTR_ERR(task);
	ret = nfs4_wait_for_completion_rpc_task(task);
	if (ret == 0) {
		ret = data->rpc_status;
4394 4395 4396
		if (ret)
			nfs4_handle_setlk_error(data->server, data->lsp,
					data->arg.new_lock_owner, ret);
4397 4398
	} else
		data->cancelled = 1;
4399
	rpc_put_task(task);
4400
	dprintk("%s: done, ret = %d!\n", __func__, ret);
4401
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
4402 4403 4404 4405
}

static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
{
4406 4407 4408 4409 4410
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs4_exception exception = { };
	int err;

	do {
4411 4412 4413
		/* Cache the lock if possible... */
		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
			return 0;
4414
		err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
4415
		if (err != -NFS4ERR_DELAY)
4416 4417 4418 4419
			break;
		nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
4420 4421 4422 4423
}

static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request)
{
4424 4425 4426 4427
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs4_exception exception = { };
	int err;

4428 4429 4430
	err = nfs4_set_lock_state(state, request);
	if (err != 0)
		return err;
4431
	do {
4432 4433
		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
			return 0;
4434
		err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED);
4435 4436 4437 4438 4439 4440 4441 4442
		switch (err) {
		default:
			goto out;
		case -NFS4ERR_GRACE:
		case -NFS4ERR_DELAY:
			nfs4_handle_exception(server, err, &exception);
			err = 0;
		}
4443
	} while (exception.retry);
4444
out:
4445
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
4446 4447
}

4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461
#if defined(CONFIG_NFS_V4_1)
static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
{
	int status;
	struct nfs_server *server = NFS_SERVER(state->inode);

	status = nfs41_test_stateid(server, state);
	if (status == NFS_OK)
		return 0;
	nfs41_free_stateid(server, state);
	return nfs4_lock_expired(state, request);
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
4462 4463
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
4464
	struct nfs_inode *nfsi = NFS_I(state->inode);
4465
	unsigned char fl_flags = request->fl_flags;
4466
	int status = -ENOLCK;
Linus Torvalds's avatar
Linus Torvalds committed
4467

4468 4469 4470
	if ((fl_flags & FL_POSIX) &&
			!test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
		goto out;
4471 4472 4473 4474
	/* Is this a delegated open? */
	status = nfs4_set_lock_state(state, request);
	if (status != 0)
		goto out;
4475 4476 4477 4478
	request->fl_flags |= FL_ACCESS;
	status = do_vfs_lock(request->fl_file, request);
	if (status < 0)
		goto out;
4479
	down_read(&nfsi->rwsem);
4480 4481 4482
	if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
		/* Yes: cache locks! */
		/* ...but avoid races with delegation recall... */
4483 4484 4485
		request->fl_flags = fl_flags & ~FL_SLEEP;
		status = do_vfs_lock(request->fl_file, request);
		goto out_unlock;
4486
	}
4487
	status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW);
4488
	if (status != 0)
4489
		goto out_unlock;
4490
	/* Note: we always want to sleep here! */
4491
	request->fl_flags = fl_flags | FL_SLEEP;
4492
	if (do_vfs_lock(request->fl_file, request) < 0)
4493
		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
4494
out_unlock:
4495
	up_read(&nfsi->rwsem);
4496 4497
out:
	request->fl_flags = fl_flags;
Linus Torvalds's avatar
Linus Torvalds committed
4498 4499 4500 4501 4502 4503 4504 4505 4506
	return status;
}

static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
	struct nfs4_exception exception = { };
	int err;

	do {
4507 4508 4509
		err = _nfs4_proc_setlk(state, cmd, request);
		if (err == -NFS4ERR_DENIED)
			err = -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
4510
		err = nfs4_handle_exception(NFS_SERVER(state->inode),
4511
				err, &exception);
Linus Torvalds's avatar
Linus Torvalds committed
4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524
	} while (exception.retry);
	return err;
}

static int
nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
{
	struct nfs_open_context *ctx;
	struct nfs4_state *state;
	unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
	int status;

	/* verify open state */
4525
	ctx = nfs_file_open_context(filp);
Linus Torvalds's avatar
Linus Torvalds committed
4526 4527 4528 4529 4530
	state = ctx->state;

	if (request->fl_start < 0 || request->fl_end < 0)
		return -EINVAL;

4531 4532 4533 4534 4535
	if (IS_GETLK(cmd)) {
		if (state != NULL)
			return nfs4_proc_getlk(state, F_GETLK, request);
		return 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
4536 4537 4538 4539

	if (!(IS_SETLK(cmd) || IS_SETLKW(cmd)))
		return -EINVAL;

4540 4541 4542 4543 4544
	if (request->fl_type == F_UNLCK) {
		if (state != NULL)
			return nfs4_proc_unlck(state, cmd, request);
		return 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
4545

4546 4547
	if (state == NULL)
		return -ENOLCK;
Linus Torvalds's avatar
Linus Torvalds committed
4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559
	do {
		status = nfs4_proc_setlk(state, cmd, request);
		if ((status != -EAGAIN) || IS_SETLK(cmd))
			break;
		timeout = nfs4_set_lock_task_retry(timeout);
		status = -ERESTARTSYS;
		if (signalled())
			break;
	} while(status < 0);
	return status;
}

4560 4561 4562 4563 4564 4565 4566 4567 4568 4569
int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
{
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs4_exception exception = { };
	int err;

	err = nfs4_set_lock_state(state, fl);
	if (err != 0)
		goto out;
	do {
4570
		err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
4571 4572 4573 4574 4575
		switch (err) {
			default:
				printk(KERN_ERR "%s: unhandled error %d.\n",
						__func__, err);
			case 0:
4576
			case -ESTALE:
4577 4578
				goto out;
			case -NFS4ERR_EXPIRED:
4579
				nfs4_schedule_stateid_recovery(server, state);
4580
			case -NFS4ERR_STALE_CLIENTID:
4581
			case -NFS4ERR_STALE_STATEID:
4582 4583
				nfs4_schedule_lease_recovery(server->nfs_client);
				goto out;
4584 4585 4586 4587 4588
			case -NFS4ERR_BADSESSION:
			case -NFS4ERR_BADSLOT:
			case -NFS4ERR_BAD_HIGH_SLOT:
			case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
			case -NFS4ERR_DEADSESSION:
4589
				nfs4_schedule_session_recovery(server->nfs_client->cl_session);
4590
				goto out;
4591 4592 4593 4594 4595 4596 4597 4598
			case -ERESTARTSYS:
				/*
				 * The show must go on: exit, but mark the
				 * stateid as needing recovery.
				 */
			case -NFS4ERR_ADMIN_REVOKED:
			case -NFS4ERR_BAD_STATEID:
			case -NFS4ERR_OPENMODE:
4599
				nfs4_schedule_stateid_recovery(server, state);
4600 4601
				err = 0;
				goto out;
4602 4603 4604 4605 4606 4607 4608 4609 4610
			case -EKEYEXPIRED:
				/*
				 * User RPCSEC_GSS context has expired.
				 * We cannot recover this stateid now, so
				 * skip it and allow recovery thread to
				 * proceed.
				 */
				err = 0;
				goto out;
4611 4612 4613 4614 4615
			case -ENOMEM:
			case -NFS4ERR_DENIED:
				/* kill_proc(fl->fl_pid, SIGLOST, 1); */
				err = 0;
				goto out;
4616 4617 4618
			case -NFS4ERR_DELAY:
				break;
		}
4619 4620 4621 4622 4623
		err = nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
out:
	return err;
}
4624

4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648
static void nfs4_release_lockowner_release(void *calldata)
{
	kfree(calldata);
}

const struct rpc_call_ops nfs4_release_lockowner_ops = {
	.rpc_release = nfs4_release_lockowner_release,
};

void nfs4_release_lockowner(const struct nfs4_lock_state *lsp)
{
	struct nfs_server *server = lsp->ls_state->owner->so_server;
	struct nfs_release_lockowner_args *args;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
	};

	if (server->nfs_client->cl_mvops->minor_version != 0)
		return;
	args = kmalloc(sizeof(*args), GFP_NOFS);
	if (!args)
		return;
	args->lock_owner.clientid = server->nfs_client->cl_clientid;
	args->lock_owner.id = lsp->ls_id.id;
4649
	args->lock_owner.s_dev = server->s_dev;
4650 4651 4652 4653
	msg.rpc_argp = args;
	rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, args);
}

4654 4655
#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"

4656 4657 4658
static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
				   const void *buf, size_t buflen,
				   int flags, int type)
4659
{
4660 4661
	if (strcmp(key, "") != 0)
		return -EINVAL;
4662

4663
	return nfs4_proc_set_acl(dentry->d_inode, buf, buflen);
4664 4665
}

4666 4667
static int nfs4_xattr_get_nfs4_acl(struct dentry *dentry, const char *key,
				   void *buf, size_t buflen, int type)
4668
{
4669 4670
	if (strcmp(key, "") != 0)
		return -EINVAL;
4671

4672
	return nfs4_proc_get_acl(dentry->d_inode, buf, buflen);
4673 4674
}

4675 4676 4677
static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
				       size_t list_len, const char *name,
				       size_t name_len, int type)
4678
{
4679
	size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
4680

4681 4682
	if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
		return 0;
4683 4684 4685

	if (list && len <= list_len)
		memcpy(list, XATTR_NAME_NFSV4_ACL, len);
4686
	return len;
4687 4688
}

4689 4690 4691
/*
 * nfs_fhget will use either the mounted_on_fileid or the fileid
 */
4692 4693
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
{
4694 4695 4696 4697
	if (!(((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) ||
	       (fattr->valid & NFS_ATTR_FATTR_FILEID)) &&
	      (fattr->valid & NFS_ATTR_FATTR_FSID) &&
	      (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)))
4698 4699 4700 4701 4702 4703 4704 4705
		return;

	fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
		NFS_ATTR_FATTR_NLINK;
	fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
	fattr->nlink = 2;
}

4706
int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
4707
		struct nfs4_fs_locations *fs_locations, struct page *page)
4708 4709 4710
{
	struct nfs_server *server = NFS_SERVER(dir);
	u32 bitmask[2] = {
4711
		[0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
4712 4713 4714
	};
	struct nfs4_fs_locations_arg args = {
		.dir_fh = NFS_FH(dir),
4715
		.name = name,
4716 4717 4718
		.page = page,
		.bitmask = bitmask,
	};
4719 4720 4721
	struct nfs4_fs_locations_res res = {
		.fs_locations = fs_locations,
	};
4722 4723 4724
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
		.rpc_argp = &args,
4725
		.rpc_resp = &res,
4726 4727 4728
	};
	int status;

4729
	dprintk("%s: start\n", __func__);
4730 4731 4732 4733 4734 4735 4736 4737

	/* Ask for the fileid of the absent filesystem if mounted_on_fileid
	 * is not supported */
	if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
		bitmask[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
	else
		bitmask[0] |= FATTR4_WORD0_FILEID;

4738
	nfs_fattr_init(&fs_locations->fattr);
4739
	fs_locations->server = server;
4740
	fs_locations->nlocations = 0;
4741
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
4742
	dprintk("%s: returned status = %d\n", __func__, status);
4743 4744 4745
	return status;
}

Bryan Schumaker's avatar
Bryan Schumaker committed
4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779
static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
{
	int status;
	struct nfs4_secinfo_arg args = {
		.dir_fh = NFS_FH(dir),
		.name   = name,
	};
	struct nfs4_secinfo_res res = {
		.flavors     = flavors,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};

	dprintk("NFS call  secinfo %s\n", name->name);
	status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
	dprintk("NFS reply  secinfo: %d\n", status);
	return status;
}

int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(NFS_SERVER(dir),
				_nfs4_proc_secinfo(dir, name, flavors),
				&exception);
	} while (exception.retry);
	return err;
}

4780
#ifdef CONFIG_NFS_V4_1
4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799
/*
 * Check the exchange flags returned by the server for invalid flags, having
 * both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or
 * DS flags set.
 */
static int nfs4_check_cl_exchange_flags(u32 flags)
{
	if (flags & ~EXCHGID4_FLAG_MASK_R)
		goto out_inval;
	if ((flags & EXCHGID4_FLAG_USE_PNFS_MDS) &&
	    (flags & EXCHGID4_FLAG_USE_NON_PNFS))
		goto out_inval;
	if (!(flags & (EXCHGID4_FLAG_MASK_PNFS)))
		goto out_inval;
	return NFS_OK;
out_inval:
	return -NFS4ERR_INVAL;
}

4800 4801 4802 4803 4804 4805 4806 4807 4808 4809
static bool
nfs41_same_server_scope(struct server_scope *a, struct server_scope *b)
{
	if (a->server_scope_sz == b->server_scope_sz &&
	    memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0)
		return true;

	return false;
}

Benny Halevy's avatar
Benny Halevy committed
4810 4811 4812 4813 4814 4815 4816 4817
/*
 * nfs4_proc_exchange_id()
 *
 * Since the clientid has expired, all compounds using sessions
 * associated with the stale clientid will be returning
 * NFS4ERR_BADSESSION in the sequence operation, and will therefore
 * be in some phase of session reset.
 */
4818
int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
Benny Halevy's avatar
Benny Halevy committed
4819 4820 4821 4822
{
	nfs4_verifier verifier;
	struct nfs41_exchange_id_args args = {
		.client = clp,
4823
		.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER,
Benny Halevy's avatar
Benny Halevy committed
4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838
	};
	struct nfs41_exchange_id_res res = {
		.client = clp,
	};
	int status;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_cred = cred,
	};
	__be32 *p;

	dprintk("--> %s\n", __func__);
	BUG_ON(clp == NULL);
4839

Benny Halevy's avatar
Benny Halevy committed
4840 4841 4842 4843 4844
	p = (u32 *)verifier.data;
	*p++ = htonl((u32)clp->cl_boot_time.tv_sec);
	*p = htonl((u32)clp->cl_boot_time.tv_nsec);
	args.verifier = &verifier;

4845 4846 4847 4848 4849 4850
	args.id_len = scnprintf(args.id, sizeof(args.id),
				"%s/%s.%s/%u",
				clp->cl_ipaddr,
				init_utsname()->nodename,
				init_utsname()->domainname,
				clp->cl_rpcclient->cl_auth->au_flavor);
Benny Halevy's avatar
Benny Halevy committed
4851

4852 4853 4854 4855
	res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL);
	if (unlikely(!res.server_scope))
		return -ENOMEM;

4856
	status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
4857 4858
	if (!status)
		status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876

	if (!status) {
		if (clp->server_scope &&
		    !nfs41_same_server_scope(clp->server_scope,
					     res.server_scope)) {
			dprintk("%s: server_scope mismatch detected\n",
				__func__);
			set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
			kfree(clp->server_scope);
			clp->server_scope = NULL;
		}

		if (!clp->server_scope)
			clp->server_scope = res.server_scope;
		else
			kfree(res.server_scope);
	}

Benny Halevy's avatar
Benny Halevy committed
4877 4878 4879 4880
	dprintk("<-- %s status= %d\n", __func__, status);
	return status;
}

Andy Adamson's avatar
Andy Adamson committed
4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894
struct nfs4_get_lease_time_data {
	struct nfs4_get_lease_time_args *args;
	struct nfs4_get_lease_time_res *res;
	struct nfs_client *clp;
};

static void nfs4_get_lease_time_prepare(struct rpc_task *task,
					void *calldata)
{
	int ret;
	struct nfs4_get_lease_time_data *data =
			(struct nfs4_get_lease_time_data *)calldata;

	dprintk("--> %s\n", __func__);
4895
	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
Andy Adamson's avatar
Andy Adamson committed
4896 4897 4898
	/* just setup sequence, do not trigger session recovery
	   since we're invoked within one */
	ret = nfs41_setup_sequence(data->clp->cl_session,
4899 4900
				   &data->args->la_seq_args,
				   &data->res->lr_seq_res, 0, task);
Andy Adamson's avatar
Andy Adamson committed
4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916

	BUG_ON(ret == -EAGAIN);
	rpc_call_start(task);
	dprintk("<-- %s\n", __func__);
}

/*
 * Called from nfs4_state_manager thread for session setup, so don't recover
 * from sequence operation or clientid errors.
 */
static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_get_lease_time_data *data =
			(struct nfs4_get_lease_time_data *)calldata;

	dprintk("--> %s\n", __func__);
4917 4918
	if (!nfs41_sequence_done(task, &data->res->lr_seq_res))
		return;
Andy Adamson's avatar
Andy Adamson committed
4919 4920 4921 4922 4923 4924
	switch (task->tk_status) {
	case -NFS4ERR_DELAY:
	case -NFS4ERR_GRACE:
		dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
		rpc_delay(task, NFS4_POLL_RETRY_MIN);
		task->tk_status = 0;
4925 4926
		/* fall through */
	case -NFS4ERR_RETRY_UNCACHED_REP:
4927
		rpc_restart_call_prepare(task);
Andy Adamson's avatar
Andy Adamson committed
4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958
		return;
	}
	dprintk("<-- %s\n", __func__);
}

struct rpc_call_ops nfs4_get_lease_time_ops = {
	.rpc_call_prepare = nfs4_get_lease_time_prepare,
	.rpc_call_done = nfs4_get_lease_time_done,
};

int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
{
	struct rpc_task *task;
	struct nfs4_get_lease_time_args args;
	struct nfs4_get_lease_time_res res = {
		.lr_fsinfo = fsinfo,
	};
	struct nfs4_get_lease_time_data data = {
		.args = &args,
		.res = &res,
		.clp = clp,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	struct rpc_task_setup task_setup = {
		.rpc_client = clp->cl_rpcclient,
		.rpc_message = &msg,
		.callback_ops = &nfs4_get_lease_time_ops,
4959 4960
		.callback_data = &data,
		.flags = RPC_TASK_TIMEOUT,
Andy Adamson's avatar
Andy Adamson committed
4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977
	};
	int status;

	dprintk("--> %s\n", __func__);
	task = rpc_run_task(&task_setup);

	if (IS_ERR(task))
		status = PTR_ERR(task);
	else {
		status = task->tk_status;
		rpc_put_task(task);
	}
	dprintk("<-- %s return %d\n", __func__, status);

	return status;
}

4978 4979 4980
/*
 * Reset a slot table
 */
4981 4982
static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs,
				 int ivalue)
4983
{
4984
	struct nfs4_slot *new = NULL;
4985
	int i;
4986 4987
	int ret = 0;

4988 4989
	dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__,
		max_reqs, tbl->max_slots);
4990

4991 4992 4993 4994
	/* Does the newly negotiated max_reqs match the existing slot table? */
	if (max_reqs != tbl->max_slots) {
		ret = -ENOMEM;
		new = kmalloc(max_reqs * sizeof(struct nfs4_slot),
4995
			      GFP_NOFS);
4996 4997 4998 4999
		if (!new)
			goto out;
		ret = 0;
		kfree(tbl->slots);
5000 5001
	}
	spin_lock(&tbl->slot_tbl_lock);
5002 5003 5004 5005 5006
	if (new) {
		tbl->slots = new;
		tbl->max_slots = max_reqs;
	}
	for (i = 0; i < tbl->max_slots; ++i)
5007
		tbl->slots[i].seq_nr = ivalue;
5008 5009 5010 5011 5012 5013 5014 5015
	spin_unlock(&tbl->slot_tbl_lock);
	dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
		tbl, tbl->slots, tbl->max_slots);
out:
	dprintk("<-- %s: return %d\n", __func__, ret);
	return ret;
}

5016 5017 5018 5019 5020 5021 5022 5023
/*
 * Reset the forechannel and backchannel slot tables
 */
static int nfs4_reset_slot_tables(struct nfs4_session *session)
{
	int status;

	status = nfs4_reset_slot_table(&session->fc_slot_table,
5024
			session->fc_attrs.max_reqs, 1);
5025 5026 5027 5028
	if (status)
		return status;

	status = nfs4_reset_slot_table(&session->bc_slot_table,
5029
			session->bc_attrs.max_reqs, 0);
5030 5031 5032
	return status;
}

5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046
/* Destroy the slot table */
static void nfs4_destroy_slot_tables(struct nfs4_session *session)
{
	if (session->fc_slot_table.slots != NULL) {
		kfree(session->fc_slot_table.slots);
		session->fc_slot_table.slots = NULL;
	}
	if (session->bc_slot_table.slots != NULL) {
		kfree(session->bc_slot_table.slots);
		session->bc_slot_table.slots = NULL;
	}
	return;
}

Andy Adamson's avatar
Andy Adamson committed
5047 5048 5049
/*
 * Initialize slot table
 */
5050 5051
static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
		int max_slots, int ivalue)
Andy Adamson's avatar
Andy Adamson committed
5052 5053 5054 5055 5056 5057
{
	struct nfs4_slot *slot;
	int ret = -ENOMEM;

	BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE);

5058
	dprintk("--> %s: max_reqs=%u\n", __func__, max_slots);
Andy Adamson's avatar
Andy Adamson committed
5059

5060
	slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_NOFS);
Andy Adamson's avatar
Andy Adamson committed
5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076
	if (!slot)
		goto out;
	ret = 0;

	spin_lock(&tbl->slot_tbl_lock);
	tbl->max_slots = max_slots;
	tbl->slots = slot;
	tbl->highest_used_slotid = -1;  /* no slot is currently used */
	spin_unlock(&tbl->slot_tbl_lock);
	dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
		tbl, tbl->slots, tbl->max_slots);
out:
	dprintk("<-- %s: return %d\n", __func__, ret);
	return ret;
}

5077 5078 5079 5080 5081
/*
 * Initialize the forechannel and backchannel tables
 */
static int nfs4_init_slot_tables(struct nfs4_session *session)
{
5082 5083
	struct nfs4_slot_table *tbl;
	int status = 0;
5084

5085 5086 5087 5088 5089 5090 5091
	tbl = &session->fc_slot_table;
	if (tbl->slots == NULL) {
		status = nfs4_init_slot_table(tbl,
				session->fc_attrs.max_reqs, 1);
		if (status)
			return status;
	}
5092

5093 5094 5095 5096 5097 5098 5099
	tbl = &session->bc_slot_table;
	if (tbl->slots == NULL) {
		status = nfs4_init_slot_table(tbl,
				session->bc_attrs.max_reqs, 0);
		if (status)
			nfs4_destroy_slot_tables(session);
	}
5100 5101

	return status;
5102 5103 5104 5105 5106 5107 5108
}

struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
{
	struct nfs4_session *session;
	struct nfs4_slot_table *tbl;

5109
	session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
5110 5111
	if (!session)
		return NULL;
5112

5113
	tbl = &session->fc_slot_table;
5114
	tbl->highest_used_slotid = -1;
5115
	spin_lock_init(&tbl->slot_tbl_lock);
5116
	rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
5117
	init_completion(&tbl->complete);
5118 5119

	tbl = &session->bc_slot_table;
5120
	tbl->highest_used_slotid = -1;
5121 5122
	spin_lock_init(&tbl->slot_tbl_lock);
	rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
5123
	init_completion(&tbl->complete);
5124

5125 5126
	session->session_state = 1<<NFS4_SESSION_INITING;

5127 5128 5129 5130 5131 5132
	session->clp = clp;
	return session;
}

void nfs4_destroy_session(struct nfs4_session *session)
{
5133 5134 5135 5136 5137
	nfs4_proc_destroy_session(session);
	dprintk("%s Destroy backchannel for xprt %p\n",
		__func__, session->clp->cl_rpcclient->cl_xprt);
	xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt,
				NFS41_BC_MIN_CALLBACKS);
5138
	nfs4_destroy_slot_tables(session);
5139 5140 5141
	kfree(session);
}

Andy Adamson's avatar
Andy Adamson committed
5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167
/*
 * Initialize the values to be used by the client in CREATE_SESSION
 * If nfs4_init_session set the fore channel request and response sizes,
 * use them.
 *
 * Set the back channel max_resp_sz_cached to zero to force the client to
 * always set csa_cachethis to FALSE because the current implementation
 * of the back channel DRC only supports caching the CB_SEQUENCE operation.
 */
static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
{
	struct nfs4_session *session = args->client->cl_session;
	unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz,
		     mxresp_sz = session->fc_attrs.max_resp_sz;

	if (mxrqst_sz == 0)
		mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
	if (mxresp_sz == 0)
		mxresp_sz = NFS_MAX_FILE_IO_SIZE;
	/* Fore channel attributes */
	args->fc_attrs.max_rqst_sz = mxrqst_sz;
	args->fc_attrs.max_resp_sz = mxresp_sz;
	args->fc_attrs.max_ops = NFS4_MAX_OPS;
	args->fc_attrs.max_reqs = session->clp->cl_rpcclient->cl_xprt->max_reqs;

	dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u "
5168
		"max_ops=%u max_reqs=%u\n",
Andy Adamson's avatar
Andy Adamson committed
5169 5170
		__func__,
		args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz,
5171
		args->fc_attrs.max_ops, args->fc_attrs.max_reqs);
Andy Adamson's avatar
Andy Adamson committed
5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187

	/* Back channel attributes */
	args->bc_attrs.max_rqst_sz = PAGE_SIZE;
	args->bc_attrs.max_resp_sz = PAGE_SIZE;
	args->bc_attrs.max_resp_sz_cached = 0;
	args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
	args->bc_attrs.max_reqs = 1;

	dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u "
		"max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
		__func__,
		args->bc_attrs.max_rqst_sz, args->bc_attrs.max_resp_sz,
		args->bc_attrs.max_resp_sz_cached, args->bc_attrs.max_ops,
		args->bc_attrs.max_reqs);
}

5188
static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session)
5189
{
5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205
	struct nfs4_channel_attrs *sent = &args->fc_attrs;
	struct nfs4_channel_attrs *rcvd = &session->fc_attrs;

	if (rcvd->max_resp_sz > sent->max_resp_sz)
		return -EINVAL;
	/*
	 * Our requested max_ops is the minimum we need; we're not
	 * prepared to break up compounds into smaller pieces than that.
	 * So, no point even trying to continue if the server won't
	 * cooperate:
	 */
	if (rcvd->max_ops < sent->max_ops)
		return -EINVAL;
	if (rcvd->max_reqs == 0)
		return -EINVAL;
	return 0;
5206 5207
}

5208 5209 5210 5211
static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session)
{
	struct nfs4_channel_attrs *sent = &args->bc_attrs;
	struct nfs4_channel_attrs *rcvd = &session->bc_attrs;
5212

5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225
	if (rcvd->max_rqst_sz > sent->max_rqst_sz)
		return -EINVAL;
	if (rcvd->max_resp_sz < sent->max_resp_sz)
		return -EINVAL;
	if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached)
		return -EINVAL;
	/* These would render the backchannel useless: */
	if (rcvd->max_ops  == 0)
		return -EINVAL;
	if (rcvd->max_reqs == 0)
		return -EINVAL;
	return 0;
}
5226 5227 5228 5229

static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args,
				     struct nfs4_session *session)
{
5230
	int ret;
5231

5232 5233 5234 5235
	ret = nfs4_verify_fore_channel_attrs(args, session);
	if (ret)
		return ret;
	return nfs4_verify_back_channel_attrs(args, session);
5236 5237
}

Andy Adamson's avatar
Andy Adamson committed
5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255
static int _nfs4_proc_create_session(struct nfs_client *clp)
{
	struct nfs4_session *session = clp->cl_session;
	struct nfs41_create_session_args args = {
		.client = clp,
		.cb_program = NFS4_CALLBACK,
	};
	struct nfs41_create_session_res res = {
		.client = clp,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	int status;

	nfs4_init_channel_attrs(&args);
5256
	args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
Andy Adamson's avatar
Andy Adamson committed
5257

5258
	status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
Andy Adamson's avatar
Andy Adamson committed
5259

5260 5261 5262
	if (!status)
		/* Verify the session's negotiated channel_attrs values */
		status = nfs4_verify_channel_attrs(&args, session);
Andy Adamson's avatar
Andy Adamson committed
5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275
	if (!status) {
		/* Increment the clientid slot sequence id */
		clp->cl_seqid++;
	}

	return status;
}

/*
 * Issues a CREATE_SESSION operation to the server.
 * It is the responsibility of the caller to verify the session is
 * expired before calling this routine.
 */
5276
int nfs4_proc_create_session(struct nfs_client *clp)
Andy Adamson's avatar
Andy Adamson committed
5277 5278 5279 5280 5281 5282 5283
{
	int status;
	unsigned *ptr;
	struct nfs4_session *session = clp->cl_session;

	dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);

5284
	status = _nfs4_proc_create_session(clp);
Andy Adamson's avatar
Andy Adamson committed
5285 5286 5287
	if (status)
		goto out;

5288 5289 5290 5291 5292 5293 5294
	/* Init and reset the fore channel */
	status = nfs4_init_slot_tables(session);
	dprintk("slot table initialization returned %d\n", status);
	if (status)
		goto out;
	status = nfs4_reset_slot_tables(session);
	dprintk("slot table reset returned %d\n", status);
Andy Adamson's avatar
Andy Adamson committed
5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305
	if (status)
		goto out;

	ptr = (unsigned *)&session->sess_id.data[0];
	dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__,
		clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]);
out:
	dprintk("<-- %s\n", __func__);
	return status;
}

5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324
/*
 * Issue the over-the-wire RPC DESTROY_SESSION.
 * The caller must serialize access to this routine.
 */
int nfs4_proc_destroy_session(struct nfs4_session *session)
{
	int status = 0;
	struct rpc_message msg;

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

	/* session is still being setup */
	if (session->clp->cl_cons_state != NFS_CS_READY)
		return status;

	msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION];
	msg.rpc_argp = session;
	msg.rpc_resp = NULL;
	msg.rpc_cred = NULL;
5325
	status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
5326 5327 5328 5329 5330 5331 5332 5333 5334 5335

	if (status)
		printk(KERN_WARNING
			"Got error %d from the server on DESTROY_SESSION. "
			"Session has been destroyed regardless...\n", status);

	dprintk("<-- nfs4_proc_destroy_session\n");
	return status;
}

5336 5337 5338
int nfs4_init_session(struct nfs_server *server)
{
	struct nfs_client *clp = server->nfs_client;
5339
	struct nfs4_session *session;
5340
	unsigned int rsize, wsize;
5341 5342 5343 5344 5345
	int ret;

	if (!nfs4_has_session(clp))
		return 0;

5346 5347 5348 5349
	session = clp->cl_session;
	if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
		return 0;

5350 5351 5352 5353 5354 5355 5356 5357 5358
	rsize = server->rsize;
	if (rsize == 0)
		rsize = NFS_MAX_FILE_IO_SIZE;
	wsize = server->wsize;
	if (wsize == 0)
		wsize = NFS_MAX_FILE_IO_SIZE;

	session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
	session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
5359

5360 5361 5362 5363 5364 5365
	ret = nfs4_recover_expired_lease(server);
	if (!ret)
		ret = nfs4_check_client_ready(clp);
	return ret;
}

Andy Adamson's avatar
Andy Adamson committed
5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386
int nfs4_init_ds_session(struct nfs_client *clp)
{
	struct nfs4_session *session = clp->cl_session;
	int ret;

	if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
		return 0;

	ret = nfs4_client_recover_expired_lease(clp);
	if (!ret)
		/* Test for the DS role */
		if (!is_ds_client(clp))
			ret = -ENODEV;
	if (!ret)
		ret = nfs4_check_client_ready(clp);
	return ret;

}
EXPORT_SYMBOL_GPL(nfs4_init_ds_session);


Andy Adamson's avatar
Andy Adamson committed
5387 5388 5389
/*
 * Renew the cl_session lease.
 */
5390 5391 5392 5393 5394 5395
struct nfs4_sequence_data {
	struct nfs_client *clp;
	struct nfs4_sequence_args args;
	struct nfs4_sequence_res res;
};

5396 5397
static void nfs41_sequence_release(void *data)
{
5398 5399
	struct nfs4_sequence_data *calldata = data;
	struct nfs_client *clp = calldata->clp;
5400

5401 5402 5403
	if (atomic_read(&clp->cl_count) > 1)
		nfs4_schedule_state_renewal(clp);
	nfs_put_client(clp);
5404
	kfree(calldata);
5405 5406
}

5407 5408 5409 5410 5411 5412 5413
static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client *clp)
{
	switch(task->tk_status) {
	case -NFS4ERR_DELAY:
		rpc_delay(task, NFS4_POLL_RETRY_MAX);
		return -EAGAIN;
	default:
5414
		nfs4_schedule_lease_recovery(clp);
5415 5416 5417 5418
	}
	return 0;
}

5419
static void nfs41_sequence_call_done(struct rpc_task *task, void *data)
Andy Adamson's avatar
Andy Adamson committed
5420
{
5421 5422
	struct nfs4_sequence_data *calldata = data;
	struct nfs_client *clp = calldata->clp;
Andy Adamson's avatar
Andy Adamson committed
5423

5424 5425
	if (!nfs41_sequence_done(task, task->tk_msg.rpc_resp))
		return;
Andy Adamson's avatar
Andy Adamson committed
5426 5427 5428

	if (task->tk_status < 0) {
		dprintk("%s ERROR %d\n", __func__, task->tk_status);
5429 5430
		if (atomic_read(&clp->cl_count) == 1)
			goto out;
Andy Adamson's avatar
Andy Adamson committed
5431

5432 5433
		if (nfs41_sequence_handle_errors(task, clp) == -EAGAIN) {
			rpc_restart_call_prepare(task);
Andy Adamson's avatar
Andy Adamson committed
5434 5435 5436 5437
			return;
		}
	}
	dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
5438
out:
Andy Adamson's avatar
Andy Adamson committed
5439 5440 5441 5442 5443
	dprintk("<-- %s\n", __func__);
}

static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
{
5444 5445
	struct nfs4_sequence_data *calldata = data;
	struct nfs_client *clp = calldata->clp;
Andy Adamson's avatar
Andy Adamson committed
5446 5447 5448 5449 5450 5451
	struct nfs4_sequence_args *args;
	struct nfs4_sequence_res *res;

	args = task->tk_msg.rpc_argp;
	res = task->tk_msg.rpc_resp;

5452
	if (nfs41_setup_sequence(clp->cl_session, args, res, 0, task))
Andy Adamson's avatar
Andy Adamson committed
5453 5454 5455 5456 5457 5458 5459
		return;
	rpc_call_start(task);
}

static const struct rpc_call_ops nfs41_sequence_ops = {
	.rpc_call_done = nfs41_sequence_call_done,
	.rpc_call_prepare = nfs41_sequence_prepare,
5460
	.rpc_release = nfs41_sequence_release,
Andy Adamson's avatar
Andy Adamson committed
5461 5462
};

5463
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
Andy Adamson's avatar
Andy Adamson committed
5464
{
5465
	struct nfs4_sequence_data *calldata;
Andy Adamson's avatar
Andy Adamson committed
5466 5467 5468 5469
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
		.rpc_cred = cred,
	};
5470 5471 5472 5473 5474 5475
	struct rpc_task_setup task_setup_data = {
		.rpc_client = clp->cl_rpcclient,
		.rpc_message = &msg,
		.callback_ops = &nfs41_sequence_ops,
		.flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
	};
Andy Adamson's avatar
Andy Adamson committed
5476

5477
	if (!atomic_inc_not_zero(&clp->cl_count))
5478
		return ERR_PTR(-EIO);
5479
	calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
5480
	if (calldata == NULL) {
5481
		nfs_put_client(clp);
5482
		return ERR_PTR(-ENOMEM);
Andy Adamson's avatar
Andy Adamson committed
5483
	}
5484 5485 5486
	msg.rpc_argp = &calldata->args;
	msg.rpc_resp = &calldata->res;
	calldata->clp = clp;
5487
	task_setup_data.callback_data = calldata;
Andy Adamson's avatar
Andy Adamson committed
5488

5489 5490 5491
	return rpc_run_task(&task_setup_data);
}

5492
static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags)
5493 5494 5495 5496
{
	struct rpc_task *task;
	int ret = 0;

5497 5498
	if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
		return 0;
5499 5500 5501 5502
	task = _nfs41_proc_sequence(clp, cred);
	if (IS_ERR(task))
		ret = PTR_ERR(task);
	else
5503
		rpc_put_task_async(task);
5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518
	dprintk("<-- %s status=%d\n", __func__, ret);
	return ret;
}

static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
{
	struct rpc_task *task;
	int ret;

	task = _nfs41_proc_sequence(clp, cred);
	if (IS_ERR(task)) {
		ret = PTR_ERR(task);
		goto out;
	}
	ret = rpc_wait_for_completion_task(task);
5519 5520 5521 5522 5523
	if (!ret) {
		struct nfs4_sequence_res *res = task->tk_msg.rpc_resp;

		if (task->tk_status == 0)
			nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
5524
		ret = task->tk_status;
5525
	}
5526 5527 5528 5529
	rpc_put_task(task);
out:
	dprintk("<-- %s status=%d\n", __func__, ret);
	return ret;
Andy Adamson's avatar
Andy Adamson committed
5530 5531
}

5532 5533 5534 5535 5536 5537 5538 5539 5540 5541
struct nfs4_reclaim_complete_data {
	struct nfs_client *clp;
	struct nfs41_reclaim_complete_args arg;
	struct nfs41_reclaim_complete_res res;
};

static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data)
{
	struct nfs4_reclaim_complete_data *calldata = data;

5542
	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
5543 5544
	if (nfs41_setup_sequence(calldata->clp->cl_session,
				&calldata->arg.seq_args,
5545 5546 5547 5548 5549 5550
				&calldata->res.seq_res, 0, task))
		return;

	rpc_call_start(task);
}

5551 5552 5553 5554 5555 5556 5557 5558 5559
static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nfs_client *clp)
{
	switch(task->tk_status) {
	case 0:
	case -NFS4ERR_COMPLETE_ALREADY:
	case -NFS4ERR_WRONG_CRED: /* What to do here? */
		break;
	case -NFS4ERR_DELAY:
		rpc_delay(task, NFS4_POLL_RETRY_MAX);
5560 5561
		/* fall through */
	case -NFS4ERR_RETRY_UNCACHED_REP:
5562 5563
		return -EAGAIN;
	default:
5564
		nfs4_schedule_lease_recovery(clp);
5565 5566 5567 5568
	}
	return 0;
}

5569 5570 5571 5572 5573 5574 5575
static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data)
{
	struct nfs4_reclaim_complete_data *calldata = data;
	struct nfs_client *clp = calldata->clp;
	struct nfs4_sequence_res *res = &calldata->res.seq_res;

	dprintk("--> %s\n", __func__);
5576 5577
	if (!nfs41_sequence_done(task, res))
		return;
5578

5579 5580 5581 5582
	if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) {
		rpc_restart_call_prepare(task);
		return;
	}
5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617
	dprintk("<-- %s\n", __func__);
}

static void nfs4_free_reclaim_complete_data(void *data)
{
	struct nfs4_reclaim_complete_data *calldata = data;

	kfree(calldata);
}

static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = {
	.rpc_call_prepare = nfs4_reclaim_complete_prepare,
	.rpc_call_done = nfs4_reclaim_complete_done,
	.rpc_release = nfs4_free_reclaim_complete_data,
};

/*
 * Issue a global reclaim complete.
 */
static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
{
	struct nfs4_reclaim_complete_data *calldata;
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE],
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = clp->cl_rpcclient,
		.rpc_message = &msg,
		.callback_ops = &nfs4_reclaim_complete_call_ops,
		.flags = RPC_TASK_ASYNC,
	};
	int status = -ENOMEM;

	dprintk("--> %s\n", __func__);
5618
	calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
5619 5620 5621 5622 5623 5624 5625 5626 5627
	if (calldata == NULL)
		goto out;
	calldata->clp = clp;
	calldata->arg.one_fs = 0;

	msg.rpc_argp = &calldata->arg;
	msg.rpc_resp = &calldata->res;
	task_setup_data.callback_data = calldata;
	task = rpc_run_task(&task_setup_data);
5628
	if (IS_ERR(task)) {
5629
		status = PTR_ERR(task);
5630 5631
		goto out;
	}
5632 5633 5634
	status = nfs4_wait_for_completion_rpc_task(task);
	if (status == 0)
		status = task->tk_status;
5635
	rpc_put_task(task);
5636
	return 0;
5637 5638 5639 5640
out:
	dprintk("<-- %s status=%d\n", __func__, status);
	return status;
}
5641 5642 5643 5644 5645

static void
nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutget *lgp = calldata;
Fred Isaman's avatar
Fred Isaman committed
5646
	struct nfs_server *server = NFS_SERVER(lgp->args.inode);
5647 5648

	dprintk("--> %s\n", __func__);
Fred Isaman's avatar
Fred Isaman committed
5649 5650 5651 5652 5653
	/* Note the is a race here, where a CB_LAYOUTRECALL can come in
	 * right now covering the LAYOUTGET we are about to send.
	 * However, that is not so catastrophic, and there seems
	 * to be no way to prevent it completely.
	 */
5654 5655 5656
	if (nfs4_setup_sequence(server, &lgp->args.seq_args,
				&lgp->res.seq_res, 0, task))
		return;
5657 5658 5659 5660 5661 5662
	if (pnfs_choose_layoutget_stateid(&lgp->args.stateid,
					  NFS_I(lgp->args.inode)->layout,
					  lgp->args.ctx->state)) {
		rpc_exit(task, NFS4_OK);
		return;
	}
5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727
	rpc_call_start(task);
}

static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutget *lgp = calldata;
	struct nfs_server *server = NFS_SERVER(lgp->args.inode);

	dprintk("--> %s\n", __func__);

	if (!nfs4_sequence_done(task, &lgp->res.seq_res))
		return;

	switch (task->tk_status) {
	case 0:
		break;
	case -NFS4ERR_LAYOUTTRYLATER:
	case -NFS4ERR_RECALLCONFLICT:
		task->tk_status = -NFS4ERR_DELAY;
		/* Fall through */
	default:
		if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
			rpc_restart_call_prepare(task);
			return;
		}
	}
	dprintk("<-- %s\n", __func__);
}

static void nfs4_layoutget_release(void *calldata)
{
	struct nfs4_layoutget *lgp = calldata;

	dprintk("--> %s\n", __func__);
	put_nfs_open_context(lgp->args.ctx);
	kfree(calldata);
	dprintk("<-- %s\n", __func__);
}

static const struct rpc_call_ops nfs4_layoutget_call_ops = {
	.rpc_call_prepare = nfs4_layoutget_prepare,
	.rpc_call_done = nfs4_layoutget_done,
	.rpc_release = nfs4_layoutget_release,
};

int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
{
	struct nfs_server *server = NFS_SERVER(lgp->args.inode);
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
		.rpc_argp = &lgp->args,
		.rpc_resp = &lgp->res,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
		.rpc_message = &msg,
		.callback_ops = &nfs4_layoutget_call_ops,
		.callback_data = lgp,
		.flags = RPC_TASK_ASYNC,
	};
	int status = 0;

	dprintk("--> %s\n", __func__);

5728
	lgp->res.layoutp = &lgp->args.layout;
5729 5730 5731 5732 5733
	lgp->res.seq_res.sr_slot = NULL;
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	status = nfs4_wait_for_completion_rpc_task(task);
Fred Isaman's avatar
Fred Isaman committed
5734 5735 5736 5737
	if (status == 0)
		status = task->tk_status;
	if (status == 0)
		status = pnfs_layout_process(lgp);
5738 5739 5740 5741 5742
	rpc_put_task(task);
	dprintk("<-- %s status=%d\n", __func__, status);
	return status;
}

Benny Halevy's avatar
Benny Halevy committed
5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758
static void
nfs4_layoutreturn_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutreturn *lrp = calldata;

	dprintk("--> %s\n", __func__);
	if (nfs41_setup_sequence(lrp->clp->cl_session, &lrp->args.seq_args,
				&lrp->res.seq_res, 0, task))
		return;
	rpc_call_start(task);
}

static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutreturn *lrp = calldata;
	struct nfs_server *server;
5759
	struct pnfs_layout_hdr *lo = lrp->args.layout;
Benny Halevy's avatar
Benny Halevy committed
5760 5761 5762 5763 5764 5765 5766 5767

	dprintk("--> %s\n", __func__);

	if (!nfs4_sequence_done(task, &lrp->res.seq_res))
		return;

	server = NFS_SERVER(lrp->args.inode);
	if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
5768
		rpc_restart_call_prepare(task);
Benny Halevy's avatar
Benny Halevy committed
5769 5770
		return;
	}
5771
	spin_lock(&lo->plh_inode->i_lock);
Benny Halevy's avatar
Benny Halevy committed
5772 5773 5774 5775 5776 5777
	if (task->tk_status == 0) {
		if (lrp->res.lrs_present) {
			pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
		} else
			BUG_ON(!list_empty(&lo->plh_segs));
	}
5778 5779
	lo->plh_block_lgets--;
	spin_unlock(&lo->plh_inode->i_lock);
Benny Halevy's avatar
Benny Halevy committed
5780 5781 5782 5783 5784 5785 5786 5787
	dprintk("<-- %s\n", __func__);
}

static void nfs4_layoutreturn_release(void *calldata)
{
	struct nfs4_layoutreturn *lrp = calldata;

	dprintk("--> %s\n", __func__);
5788
	put_layout_hdr(lrp->args.layout);
Benny Halevy's avatar
Benny Halevy committed
5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824
	kfree(calldata);
	dprintk("<-- %s\n", __func__);
}

static const struct rpc_call_ops nfs4_layoutreturn_call_ops = {
	.rpc_call_prepare = nfs4_layoutreturn_prepare,
	.rpc_call_done = nfs4_layoutreturn_done,
	.rpc_release = nfs4_layoutreturn_release,
};

int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp)
{
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN],
		.rpc_argp = &lrp->args,
		.rpc_resp = &lrp->res,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = lrp->clp->cl_rpcclient,
		.rpc_message = &msg,
		.callback_ops = &nfs4_layoutreturn_call_ops,
		.callback_data = lrp,
	};
	int status;

	dprintk("--> %s\n", __func__);
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	status = task->tk_status;
	dprintk("<-- %s status=%d\n", __func__, status);
	rpc_put_task(task);
	return status;
}

Andy Adamson's avatar
Andy Adamson committed
5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872
/*
 * Retrieve the list of Data Server devices from the MDS.
 */
static int _nfs4_getdevicelist(struct nfs_server *server,
				    const struct nfs_fh *fh,
				    struct pnfs_devicelist *devlist)
{
	struct nfs4_getdevicelist_args args = {
		.fh = fh,
		.layoutclass = server->pnfs_curr_ld->id,
	};
	struct nfs4_getdevicelist_res res = {
		.devlist = devlist,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICELIST],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	int status;

	dprintk("--> %s\n", __func__);
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
				&res.seq_res, 0);
	dprintk("<-- %s status=%d\n", __func__, status);
	return status;
}

int nfs4_proc_getdevicelist(struct nfs_server *server,
			    const struct nfs_fh *fh,
			    struct pnfs_devicelist *devlist)
{
	struct nfs4_exception exception = { };
	int err;

	do {
		err = nfs4_handle_exception(server,
				_nfs4_getdevicelist(server, fh, devlist),
				&exception);
	} while (exception.retry);

	dprintk("%s: err=%d, num_devs=%u\n", __func__,
		err, devlist->num_devs);

	return err;
}
EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist);

5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889
static int
_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
{
	struct nfs4_getdeviceinfo_args args = {
		.pdev = pdev,
	};
	struct nfs4_getdeviceinfo_res res = {
		.pdev = pdev,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	int status;

	dprintk("--> %s\n", __func__);
5890
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909
	dprintk("<-- %s status=%d\n", __func__, status);

	return status;
}

int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
{
	struct nfs4_exception exception = { };
	int err;

	do {
		err = nfs4_handle_exception(server,
					_nfs4_proc_getdeviceinfo(server, pdev),
					&exception);
	} while (exception.retry);
	return err;
}
EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo);

Andy Adamson's avatar
Andy Adamson committed
5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938
static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutcommit_data *data = calldata;
	struct nfs_server *server = NFS_SERVER(data->args.inode);

	if (nfs4_setup_sequence(server, &data->args.seq_args,
				&data->res.seq_res, 1, task))
		return;
	rpc_call_start(task);
}

static void
nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutcommit_data *data = calldata;
	struct nfs_server *server = NFS_SERVER(data->args.inode);

	if (!nfs4_sequence_done(task, &data->res.seq_res))
		return;

	switch (task->tk_status) { /* Just ignore these failures */
	case NFS4ERR_DELEG_REVOKED: /* layout was recalled */
	case NFS4ERR_BADIOMODE:     /* no IOMODE_RW layout for range */
	case NFS4ERR_BADLAYOUT:     /* no layout */
	case NFS4ERR_GRACE:	    /* loca_recalim always false */
		task->tk_status = 0;
	}

	if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
5939
		rpc_restart_call_prepare(task);
Andy Adamson's avatar
Andy Adamson committed
5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950
		return;
	}

	if (task->tk_status == 0)
		nfs_post_op_update_inode_force_wcc(data->args.inode,
						   data->res.fattr);
}

static void nfs4_layoutcommit_release(void *calldata)
{
	struct nfs4_layoutcommit_data *data = calldata;
5951
	struct pnfs_layout_segment *lseg, *tmp;
Peng Tao's avatar
Peng Tao committed
5952
	unsigned long *bitlock = &NFS_I(data->args.inode)->flags;
Andy Adamson's avatar
Andy Adamson committed
5953

Andy Adamson's avatar
Andy Adamson committed
5954
	pnfs_cleanup_layoutcommit(data);
Andy Adamson's avatar
Andy Adamson committed
5955
	/* Matched by references in pnfs_set_layoutcommit */
5956 5957 5958 5959 5960 5961
	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))
			put_lseg(lseg);
	}
Peng Tao's avatar
Peng Tao committed
5962 5963 5964 5965 5966

	clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock);
	smp_mb__after_clear_bit();
	wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING);

Andy Adamson's avatar
Andy Adamson committed
5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977
	put_rpccred(data->cred);
	kfree(data);
}

static const struct rpc_call_ops nfs4_layoutcommit_ops = {
	.rpc_call_prepare = nfs4_layoutcommit_prepare,
	.rpc_call_done = nfs4_layoutcommit_done,
	.rpc_release = nfs4_layoutcommit_release,
};

int
5978
nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
Andy Adamson's avatar
Andy Adamson committed
5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTCOMMIT],
		.rpc_argp = &data->args,
		.rpc_resp = &data->res,
		.rpc_cred = data->cred,
	};
	struct rpc_task_setup task_setup_data = {
		.task = &data->task,
		.rpc_client = NFS_CLIENT(data->args.inode),
		.rpc_message = &msg,
		.callback_ops = &nfs4_layoutcommit_ops,
		.callback_data = data,
		.flags = RPC_TASK_ASYNC,
	};
	struct rpc_task *task;
	int status = 0;

	dprintk("NFS: %4d initiating layoutcommit call. sync %d "
		"lbw: %llu inode %lu\n",
		data->task.tk_pid, sync,
		data->args.lastbytewritten,
		data->args.inode->i_ino);

	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
6006
	if (sync == false)
Andy Adamson's avatar
Andy Adamson committed
6007 6008 6009 6010 6011 6012 6013 6014 6015 6016
		goto out;
	status = nfs4_wait_for_completion_rpc_task(task);
	if (status != 0)
		goto out;
	status = task->tk_status;
out:
	dprintk("%s: status %d\n", __func__, status);
	rpc_put_task(task);
	return status;
}
6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095

static int
_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
		    struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
{
	struct nfs41_secinfo_no_name_args args = {
		.style = SECINFO_STYLE_CURRENT_FH,
	};
	struct nfs4_secinfo_res res = {
		.flavors = flavors,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
}

static int
nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
			   struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
		switch (err) {
		case 0:
		case -NFS4ERR_WRONGSEC:
		case -NFS4ERR_NOTSUPP:
			break;
		default:
			err = nfs4_handle_exception(server, err, &exception);
		}
	} while (exception.retry);
	return err;
}

static int
nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
		    struct nfs_fsinfo *info)
{
	int err;
	struct page *page;
	rpc_authflavor_t flavor;
	struct nfs4_secinfo_flavors *flavors;

	page = alloc_page(GFP_KERNEL);
	if (!page) {
		err = -ENOMEM;
		goto out;
	}

	flavors = page_address(page);
	err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);

	/*
	 * Fall back on "guess and check" method if
	 * the server doesn't support SECINFO_NO_NAME
	 */
	if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
		err = nfs4_find_root_sec(server, fhandle, info);
		goto out_freepage;
	}
	if (err)
		goto out_freepage;

	flavor = nfs_find_best_sec(flavors);
	if (err == 0)
		err = nfs4_lookup_root_sec(server, fhandle, info, flavor);

out_freepage:
	put_page(page);
	if (err == -EACCES)
		return -EPERM;
out:
	return err;
}
Bryan Schumaker's avatar
Bryan Schumaker committed
6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123
static int _nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
{
	int status;
	struct nfs41_test_stateid_args args = {
		.stateid = &state->stateid,
	};
	struct nfs41_test_stateid_res res;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	args.seq_args.sa_session = res.seq_res.sr_session = NULL;
	status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
	return status;
}

static int nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(server,
				_nfs41_test_stateid(server, state),
				&exception);
	} while (exception.retry);
	return err;
}
Bryan Schumaker's avatar
Bryan Schumaker committed
6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153

static int _nfs4_free_stateid(struct nfs_server *server, struct nfs4_state *state)
{
	int status;
	struct nfs41_free_stateid_args args = {
		.stateid = &state->stateid,
	};
	struct nfs41_free_stateid_res res;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};

	args.seq_args.sa_session = res.seq_res.sr_session = NULL;
	status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
	return status;
}

static int nfs41_free_stateid(struct nfs_server *server, struct nfs4_state *state)
{
	struct nfs4_exception exception = { };
	int err;
	do {
		err = nfs4_handle_exception(server,
				_nfs4_free_stateid(server, state),
				&exception);
	} while (exception.retry);
	return err;
}
6154 6155
#endif /* CONFIG_NFS_V4_1 */

6156
struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
6157
	.owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
6158
	.state_flag_bit	= NFS_STATE_RECLAIM_REBOOT,
Linus Torvalds's avatar
Linus Torvalds committed
6159 6160
	.recover_open	= nfs4_open_reclaim,
	.recover_lock	= nfs4_lock_reclaim,
6161
	.establish_clid = nfs4_init_clientid,
6162
	.get_clid_cred	= nfs4_get_setclientid_cred,
Linus Torvalds's avatar
Linus Torvalds committed
6163 6164
};

6165 6166 6167 6168 6169 6170
#if defined(CONFIG_NFS_V4_1)
struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
	.owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
	.state_flag_bit	= NFS_STATE_RECLAIM_REBOOT,
	.recover_open	= nfs4_open_reclaim,
	.recover_lock	= nfs4_lock_reclaim,
6171
	.establish_clid = nfs41_init_clientid,
6172
	.get_clid_cred	= nfs4_get_exchange_id_cred,
6173
	.reclaim_complete = nfs41_proc_reclaim_complete,
6174 6175 6176 6177 6178 6179 6180 6181 6182
};
#endif /* CONFIG_NFS_V4_1 */

struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
	.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
	.state_flag_bit	= NFS_STATE_RECLAIM_NOGRACE,
	.recover_open	= nfs4_open_expired,
	.recover_lock	= nfs4_lock_expired,
	.establish_clid = nfs4_init_clientid,
6183
	.get_clid_cred	= nfs4_get_setclientid_cred,
6184 6185 6186 6187
};

#if defined(CONFIG_NFS_V4_1)
struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
6188
	.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
6189
	.state_flag_bit	= NFS_STATE_RECLAIM_NOGRACE,
6190 6191
	.recover_open	= nfs41_open_expired,
	.recover_lock	= nfs41_lock_expired,
6192
	.establish_clid = nfs41_init_clientid,
6193
	.get_clid_cred	= nfs4_get_exchange_id_cred,
Linus Torvalds's avatar
Linus Torvalds committed
6194
};
6195
#endif /* CONFIG_NFS_V4_1 */
Linus Torvalds's avatar
Linus Torvalds committed
6196

Benny Halevy's avatar
Benny Halevy committed
6197 6198
struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = {
	.sched_state_renewal = nfs4_proc_async_renew,
6199
	.get_state_renewal_cred_locked = nfs4_get_renew_cred_locked,
6200
	.renew_lease = nfs4_proc_renew,
Benny Halevy's avatar
Benny Halevy committed
6201 6202 6203 6204 6205
};

#if defined(CONFIG_NFS_V4_1)
struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
	.sched_state_renewal = nfs41_proc_async_sequence,
6206
	.get_state_renewal_cred_locked = nfs4_get_machine_cred_locked,
6207
	.renew_lease = nfs4_proc_sequence,
Benny Halevy's avatar
Benny Halevy committed
6208 6209 6210
};
#endif

6211 6212 6213
static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
	.minor_version = 0,
	.call_sync = _nfs4_call_sync,
6214
	.validate_stateid = nfs4_validate_delegation_stateid,
6215
	.find_root_sec = nfs4_find_root_sec,
6216 6217 6218
	.reboot_recovery_ops = &nfs40_reboot_recovery_ops,
	.nograce_recovery_ops = &nfs40_nograce_recovery_ops,
	.state_renewal_ops = &nfs40_state_renewal_ops,
6219 6220 6221 6222 6223 6224
};

#if defined(CONFIG_NFS_V4_1)
static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
	.minor_version = 1,
	.call_sync = _nfs4_call_sync_session,
6225
	.validate_stateid = nfs41_validate_delegation_stateid,
6226
	.find_root_sec = nfs41_find_root_sec,
6227 6228 6229
	.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
	.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
	.state_renewal_ops = &nfs41_state_renewal_ops,
6230 6231 6232 6233 6234 6235 6236 6237 6238 6239
};
#endif

const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
	[0] = &nfs_v4_0_minor_ops,
#if defined(CONFIG_NFS_V4_1)
	[1] = &nfs_v4_1_minor_ops,
#endif
};

6240
static const struct inode_operations nfs4_file_inode_operations = {
6241 6242 6243
	.permission	= nfs_permission,
	.getattr	= nfs_getattr,
	.setattr	= nfs_setattr,
6244 6245 6246 6247
	.getxattr	= generic_getxattr,
	.setxattr	= generic_setxattr,
	.listxattr	= generic_listxattr,
	.removexattr	= generic_removexattr,
6248 6249
};

6250
const struct nfs_rpc_ops nfs_v4_clientops = {
Linus Torvalds's avatar
Linus Torvalds committed
6251 6252 6253
	.version	= 4,			/* protocol version */
	.dentry_ops	= &nfs4_dentry_operations,
	.dir_inode_ops	= &nfs4_dir_inode_operations,
6254
	.file_inode_ops	= &nfs4_file_inode_operations,
6255
	.file_ops	= &nfs4_file_operations,
Linus Torvalds's avatar
Linus Torvalds committed
6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266
	.getroot	= nfs4_proc_get_root,
	.getattr	= nfs4_proc_getattr,
	.setattr	= nfs4_proc_setattr,
	.lookup		= nfs4_proc_lookup,
	.access		= nfs4_proc_access,
	.readlink	= nfs4_proc_readlink,
	.create		= nfs4_proc_create,
	.remove		= nfs4_proc_remove,
	.unlink_setup	= nfs4_proc_unlink_setup,
	.unlink_done	= nfs4_proc_unlink_done,
	.rename		= nfs4_proc_rename,
6267 6268
	.rename_setup	= nfs4_proc_rename_setup,
	.rename_done	= nfs4_proc_rename_done,
Linus Torvalds's avatar
Linus Torvalds committed
6269 6270 6271 6272 6273 6274 6275 6276 6277
	.link		= nfs4_proc_link,
	.symlink	= nfs4_proc_symlink,
	.mkdir		= nfs4_proc_mkdir,
	.rmdir		= nfs4_proc_remove,
	.readdir	= nfs4_proc_readdir,
	.mknod		= nfs4_proc_mknod,
	.statfs		= nfs4_proc_statfs,
	.fsinfo		= nfs4_proc_fsinfo,
	.pathconf	= nfs4_proc_pathconf,
6278
	.set_capabilities = nfs4_server_capabilities,
Linus Torvalds's avatar
Linus Torvalds committed
6279 6280
	.decode_dirent	= nfs4_decode_dirent,
	.read_setup	= nfs4_proc_read_setup,
6281
	.read_done	= nfs4_read_done,
Linus Torvalds's avatar
Linus Torvalds committed
6282
	.write_setup	= nfs4_proc_write_setup,
6283
	.write_done	= nfs4_write_done,
Linus Torvalds's avatar
Linus Torvalds committed
6284
	.commit_setup	= nfs4_proc_commit_setup,
6285
	.commit_done	= nfs4_commit_done,
Linus Torvalds's avatar
Linus Torvalds committed
6286
	.lock		= nfs4_proc_lock,
6287
	.clear_acl_cache = nfs4_zap_acl_attr,
Trond Myklebust's avatar
Trond Myklebust committed
6288
	.close_context  = nfs4_close_context,
6289
	.open_context	= nfs4_atomic_open,
6290
	.init_client	= nfs4_init_client,
Bryan Schumaker's avatar
Bryan Schumaker committed
6291
	.secinfo	= nfs4_proc_secinfo,
Linus Torvalds's avatar
Linus Torvalds committed
6292 6293
};

6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
	.prefix	= XATTR_NAME_NFSV4_ACL,
	.list	= nfs4_xattr_list_nfs4_acl,
	.get	= nfs4_xattr_get_nfs4_acl,
	.set	= nfs4_xattr_set_nfs4_acl,
};

const struct xattr_handler *nfs4_xattr_handlers[] = {
	&nfs4_xattr_nfs4_acl_handler,
	NULL
};

Linus Torvalds's avatar
Linus Torvalds committed
6306 6307 6308 6309 6310
/*
 * Local variables:
 *  c-basic-offset: 8
 * End:
 */