11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (c) 2001 The Regents of the University of Michigan. 31da177e4SLinus Torvalds * All rights reserved. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Kendrick Smith <kmsmith@umich.edu> 61da177e4SLinus Torvalds * Andy Adamson <kandros@umich.edu> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or without 91da177e4SLinus Torvalds * modification, are permitted provided that the following conditions 101da177e4SLinus Torvalds * are met: 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * 1. Redistributions of source code must retain the above copyright 131da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer. 141da177e4SLinus Torvalds * 2. Redistributions in binary form must reproduce the above copyright 151da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer in the 161da177e4SLinus Torvalds * documentation and/or other materials provided with the distribution. 171da177e4SLinus Torvalds * 3. Neither the name of the University nor the names of its 181da177e4SLinus Torvalds * contributors may be used to endorse or promote products derived 191da177e4SLinus Torvalds * from this software without specific prior written permission. 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 221da177e4SLinus Torvalds * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 231da177e4SLinus Torvalds * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 241da177e4SLinus Torvalds * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251da177e4SLinus Torvalds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 261da177e4SLinus Torvalds * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 271da177e4SLinus Torvalds * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 281da177e4SLinus Torvalds * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 291da177e4SLinus Torvalds * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 301da177e4SLinus Torvalds * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 311da177e4SLinus Torvalds * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 321da177e4SLinus Torvalds * 331da177e4SLinus Torvalds */ 341da177e4SLinus Torvalds 35aceaf78dSDave Hansen #include <linux/file.h> 361da177e4SLinus Torvalds #include <linux/smp_lock.h> 375a0e3ad6STejun Heo #include <linux/slab.h> 380964a3d3SNeilBrown #include <linux/namei.h> 39c2f1a551SMeelap Shah #include <linux/swap.h> 4068e76ad0SOlga Kornievskaia #include <linux/sunrpc/svcauth_gss.h> 41363168b4SJeff Layton #include <linux/sunrpc/clnt.h> 429a74af21SBoaz Harrosh #include "xdr4.h" 430a3adadeSJ. Bruce Fields #include "vfs.h" 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds #define NFSDDBG_FACILITY NFSDDBG_PROC 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds /* Globals */ 48cf07d2eaSJ. Bruce Fields time_t nfsd4_lease = 90; /* default lease time */ 49efc4bb4fSJ. Bruce Fields time_t nfsd4_grace = 90; 50fd39ca9aSNeilBrown static time_t boot_time; 511da177e4SLinus Torvalds static u32 current_ownerid = 1; 521da177e4SLinus Torvalds static u32 current_fileid = 1; 531da177e4SLinus Torvalds static u32 current_delegid = 1; 54fd39ca9aSNeilBrown static stateid_t zerostateid; /* bits all 0 */ 55fd39ca9aSNeilBrown static stateid_t onestateid; /* bits all 1 */ 56ec6b5d7bSAndy Adamson static u64 current_sessionid = 1; 57fd39ca9aSNeilBrown 58fd39ca9aSNeilBrown #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) 59fd39ca9aSNeilBrown #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* forward declarations */ 62fd39ca9aSNeilBrown static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); 631da177e4SLinus Torvalds static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); 640964a3d3SNeilBrown static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; 650964a3d3SNeilBrown static void nfs4_set_recdir(char *recdir); 661da177e4SLinus Torvalds 678b671b80SJ. Bruce Fields /* Locking: */ 688b671b80SJ. Bruce Fields 698b671b80SJ. Bruce Fields /* Currently used for almost all code touching nfsv4 state: */ 70353ab6e9SIngo Molnar static DEFINE_MUTEX(client_mutex); 711da177e4SLinus Torvalds 728b671b80SJ. Bruce Fields /* 738b671b80SJ. Bruce Fields * Currently used for the del_recall_lru and file hash table. In an 748b671b80SJ. Bruce Fields * effort to decrease the scope of the client_mutex, this spinlock may 758b671b80SJ. Bruce Fields * eventually cover more: 768b671b80SJ. Bruce Fields */ 778b671b80SJ. Bruce Fields static DEFINE_SPINLOCK(recall_lock); 788b671b80SJ. Bruce Fields 79e18b890bSChristoph Lameter static struct kmem_cache *stateowner_slab = NULL; 80e18b890bSChristoph Lameter static struct kmem_cache *file_slab = NULL; 81e18b890bSChristoph Lameter static struct kmem_cache *stateid_slab = NULL; 82e18b890bSChristoph Lameter static struct kmem_cache *deleg_slab = NULL; 83e60d4398SNeilBrown 841da177e4SLinus Torvalds void 851da177e4SLinus Torvalds nfs4_lock_state(void) 861da177e4SLinus Torvalds { 87353ab6e9SIngo Molnar mutex_lock(&client_mutex); 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds void 911da177e4SLinus Torvalds nfs4_unlock_state(void) 921da177e4SLinus Torvalds { 93353ab6e9SIngo Molnar mutex_unlock(&client_mutex); 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds static inline u32 971da177e4SLinus Torvalds opaque_hashval(const void *ptr, int nbytes) 981da177e4SLinus Torvalds { 991da177e4SLinus Torvalds unsigned char *cptr = (unsigned char *) ptr; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds u32 x = 0; 1021da177e4SLinus Torvalds while (nbytes--) { 1031da177e4SLinus Torvalds x *= 37; 1041da177e4SLinus Torvalds x += *cptr++; 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds return x; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static struct list_head del_recall_lru; 1101da177e4SLinus Torvalds 11113cd2184SNeilBrown static inline void 11213cd2184SNeilBrown put_nfs4_file(struct nfs4_file *fi) 11313cd2184SNeilBrown { 1148b671b80SJ. Bruce Fields if (atomic_dec_and_lock(&fi->fi_ref, &recall_lock)) { 1158b671b80SJ. Bruce Fields list_del(&fi->fi_hash); 1168b671b80SJ. Bruce Fields spin_unlock(&recall_lock); 1178b671b80SJ. Bruce Fields iput(fi->fi_inode); 1188b671b80SJ. Bruce Fields kmem_cache_free(file_slab, fi); 1198b671b80SJ. Bruce Fields } 12013cd2184SNeilBrown } 12113cd2184SNeilBrown 12213cd2184SNeilBrown static inline void 12313cd2184SNeilBrown get_nfs4_file(struct nfs4_file *fi) 12413cd2184SNeilBrown { 1258b671b80SJ. Bruce Fields atomic_inc(&fi->fi_ref); 12613cd2184SNeilBrown } 12713cd2184SNeilBrown 128ef0f3390SNeilBrown static int num_delegations; 129c2f1a551SMeelap Shah unsigned int max_delegations; 130ef0f3390SNeilBrown 131ef0f3390SNeilBrown /* 132ef0f3390SNeilBrown * Open owner state (share locks) 133ef0f3390SNeilBrown */ 134ef0f3390SNeilBrown 135ef0f3390SNeilBrown /* hash tables for nfs4_stateowner */ 136ef0f3390SNeilBrown #define OWNER_HASH_BITS 8 137ef0f3390SNeilBrown #define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS) 138ef0f3390SNeilBrown #define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1) 139ef0f3390SNeilBrown 140ef0f3390SNeilBrown #define ownerid_hashval(id) \ 141ef0f3390SNeilBrown ((id) & OWNER_HASH_MASK) 142ef0f3390SNeilBrown #define ownerstr_hashval(clientid, ownername) \ 143ef0f3390SNeilBrown (((clientid) + opaque_hashval((ownername.data), (ownername.len))) & OWNER_HASH_MASK) 144ef0f3390SNeilBrown 145ef0f3390SNeilBrown static struct list_head ownerid_hashtbl[OWNER_HASH_SIZE]; 146ef0f3390SNeilBrown static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; 147ef0f3390SNeilBrown 148ef0f3390SNeilBrown /* hash table for nfs4_file */ 149ef0f3390SNeilBrown #define FILE_HASH_BITS 8 150ef0f3390SNeilBrown #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) 151ef0f3390SNeilBrown #define FILE_HASH_MASK (FILE_HASH_SIZE - 1) 152ef0f3390SNeilBrown /* hash table for (open)nfs4_stateid */ 153ef0f3390SNeilBrown #define STATEID_HASH_BITS 10 154ef0f3390SNeilBrown #define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS) 155ef0f3390SNeilBrown #define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1) 156ef0f3390SNeilBrown 157ef0f3390SNeilBrown #define file_hashval(x) \ 158ef0f3390SNeilBrown hash_ptr(x, FILE_HASH_BITS) 159ef0f3390SNeilBrown #define stateid_hashval(owner_id, file_id) \ 160ef0f3390SNeilBrown (((owner_id) + (file_id)) & STATEID_HASH_MASK) 161ef0f3390SNeilBrown 162ef0f3390SNeilBrown static struct list_head file_hashtbl[FILE_HASH_SIZE]; 163ef0f3390SNeilBrown static struct list_head stateid_hashtbl[STATEID_HASH_SIZE]; 164ef0f3390SNeilBrown 165998db52cSJ. Bruce Fields static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) 166f9d7562fSJ. Bruce Fields { 167f9d7562fSJ. Bruce Fields BUG_ON(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); 168f9d7562fSJ. Bruce Fields atomic_inc(&fp->fi_access[oflag]); 169f9d7562fSJ. Bruce Fields } 170f9d7562fSJ. Bruce Fields 171998db52cSJ. Bruce Fields static void nfs4_file_get_access(struct nfs4_file *fp, int oflag) 172998db52cSJ. Bruce Fields { 173998db52cSJ. Bruce Fields if (oflag == O_RDWR) { 174998db52cSJ. Bruce Fields __nfs4_file_get_access(fp, O_RDONLY); 175998db52cSJ. Bruce Fields __nfs4_file_get_access(fp, O_WRONLY); 176998db52cSJ. Bruce Fields } else 177998db52cSJ. Bruce Fields __nfs4_file_get_access(fp, oflag); 178998db52cSJ. Bruce Fields } 179998db52cSJ. Bruce Fields 180998db52cSJ. Bruce Fields static void nfs4_file_put_fd(struct nfs4_file *fp, int oflag) 181f9d7562fSJ. Bruce Fields { 182f9d7562fSJ. Bruce Fields if (fp->fi_fds[oflag]) { 183f9d7562fSJ. Bruce Fields fput(fp->fi_fds[oflag]); 184f9d7562fSJ. Bruce Fields fp->fi_fds[oflag] = NULL; 185f9d7562fSJ. Bruce Fields } 186f9d7562fSJ. Bruce Fields } 187f9d7562fSJ. Bruce Fields 188998db52cSJ. Bruce Fields static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag) 189f9d7562fSJ. Bruce Fields { 190f9d7562fSJ. Bruce Fields if (atomic_dec_and_test(&fp->fi_access[oflag])) { 191f9d7562fSJ. Bruce Fields nfs4_file_put_fd(fp, O_RDWR); 192f9d7562fSJ. Bruce Fields nfs4_file_put_fd(fp, oflag); 193f9d7562fSJ. Bruce Fields } 194f9d7562fSJ. Bruce Fields } 195f9d7562fSJ. Bruce Fields 196998db52cSJ. Bruce Fields static void nfs4_file_put_access(struct nfs4_file *fp, int oflag) 197998db52cSJ. Bruce Fields { 198998db52cSJ. Bruce Fields if (oflag == O_RDWR) { 199998db52cSJ. Bruce Fields __nfs4_file_put_access(fp, O_RDONLY); 200998db52cSJ. Bruce Fields __nfs4_file_put_access(fp, O_WRONLY); 201998db52cSJ. Bruce Fields } else 202998db52cSJ. Bruce Fields __nfs4_file_put_access(fp, oflag); 203998db52cSJ. Bruce Fields } 204998db52cSJ. Bruce Fields 2051da177e4SLinus Torvalds static struct nfs4_delegation * 2061da177e4SLinus Torvalds alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds struct nfs4_delegation *dp; 2091da177e4SLinus Torvalds struct nfs4_file *fp = stp->st_file; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds dprintk("NFSD alloc_init_deleg\n"); 212c3e48080SJ. Bruce Fields /* 213c3e48080SJ. Bruce Fields * Major work on the lease subsystem (for example, to support 214c3e48080SJ. Bruce Fields * calbacks on stat) will be required before we can support 215c3e48080SJ. Bruce Fields * write delegations properly. 216c3e48080SJ. Bruce Fields */ 217c3e48080SJ. Bruce Fields if (type != NFS4_OPEN_DELEGATE_READ) 218c3e48080SJ. Bruce Fields return NULL; 21947f9940cSMeelap Shah if (fp->fi_had_conflict) 22047f9940cSMeelap Shah return NULL; 221c2f1a551SMeelap Shah if (num_delegations > max_delegations) 222ef0f3390SNeilBrown return NULL; 2235b2d21c1SNeilBrown dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); 2245b2d21c1SNeilBrown if (dp == NULL) 2251da177e4SLinus Torvalds return dp; 226ef0f3390SNeilBrown num_delegations++; 227ea1da636SNeilBrown INIT_LIST_HEAD(&dp->dl_perfile); 228ea1da636SNeilBrown INIT_LIST_HEAD(&dp->dl_perclnt); 2291da177e4SLinus Torvalds INIT_LIST_HEAD(&dp->dl_recall_lru); 2301da177e4SLinus Torvalds dp->dl_client = clp; 23113cd2184SNeilBrown get_nfs4_file(fp); 2321da177e4SLinus Torvalds dp->dl_file = fp; 233f9d7562fSJ. Bruce Fields nfs4_file_get_access(fp, O_RDONLY); 2341da177e4SLinus Torvalds dp->dl_flock = NULL; 2351da177e4SLinus Torvalds dp->dl_type = type; 236e4e83ea4SJ. Bruce Fields dp->dl_stateid.si_boot = boot_time; 2371da177e4SLinus Torvalds dp->dl_stateid.si_stateownerid = current_delegid++; 2381da177e4SLinus Torvalds dp->dl_stateid.si_fileid = 0; 2391da177e4SLinus Torvalds dp->dl_stateid.si_generation = 0; 2406c02eaa1SJ. Bruce Fields fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); 2411da177e4SLinus Torvalds dp->dl_time = 0; 2421da177e4SLinus Torvalds atomic_set(&dp->dl_count, 1); 243ea1da636SNeilBrown list_add(&dp->dl_perfile, &fp->fi_delegations); 244ea1da636SNeilBrown list_add(&dp->dl_perclnt, &clp->cl_delegations); 245b5a1a81eSJ. Bruce Fields INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); 2461da177e4SLinus Torvalds return dp; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds void 2501da177e4SLinus Torvalds nfs4_put_delegation(struct nfs4_delegation *dp) 2511da177e4SLinus Torvalds { 2521da177e4SLinus Torvalds if (atomic_dec_and_test(&dp->dl_count)) { 2531da177e4SLinus Torvalds dprintk("NFSD: freeing dp %p\n",dp); 25413cd2184SNeilBrown put_nfs4_file(dp->dl_file); 2555b2d21c1SNeilBrown kmem_cache_free(deleg_slab, dp); 256ef0f3390SNeilBrown num_delegations--; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds /* Remove the associated file_lock first, then remove the delegation. 2611da177e4SLinus Torvalds * lease_modify() is called to remove the FS_LEASE file_lock from 2621da177e4SLinus Torvalds * the i_flock list, eventually calling nfsd's lock_manager 2631da177e4SLinus Torvalds * fl_release_callback. 2641da177e4SLinus Torvalds */ 2651da177e4SLinus Torvalds static void 2661da177e4SLinus Torvalds nfs4_close_delegation(struct nfs4_delegation *dp) 2671da177e4SLinus Torvalds { 268f9d7562fSJ. Bruce Fields struct file *filp = find_readable_file(dp->dl_file); 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds dprintk("NFSD: close_delegation dp %p\n",dp); 271c907132dSNeilBrown if (dp->dl_flock) 272a9933ceaSJ. Bruce Fields vfs_setlease(filp, F_UNLCK, &dp->dl_flock); 273f9d7562fSJ. Bruce Fields nfs4_file_put_access(dp->dl_file, O_RDONLY); 2741da177e4SLinus Torvalds } 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds /* Called under the state lock. */ 2771da177e4SLinus Torvalds static void 2781da177e4SLinus Torvalds unhash_delegation(struct nfs4_delegation *dp) 2791da177e4SLinus Torvalds { 280ea1da636SNeilBrown list_del_init(&dp->dl_perfile); 281ea1da636SNeilBrown list_del_init(&dp->dl_perclnt); 2821da177e4SLinus Torvalds spin_lock(&recall_lock); 2831da177e4SLinus Torvalds list_del_init(&dp->dl_recall_lru); 2841da177e4SLinus Torvalds spin_unlock(&recall_lock); 2851da177e4SLinus Torvalds nfs4_close_delegation(dp); 2861da177e4SLinus Torvalds nfs4_put_delegation(dp); 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds /* 2901da177e4SLinus Torvalds * SETCLIENTID state 2911da177e4SLinus Torvalds */ 2921da177e4SLinus Torvalds 29336acb66bSBenny Halevy /* client_lock protects the client lru list and session hash table */ 2949089f1b4SBenny Halevy static DEFINE_SPINLOCK(client_lock); 2959089f1b4SBenny Halevy 2961da177e4SLinus Torvalds /* Hash tables for nfs4_clientid state */ 2971da177e4SLinus Torvalds #define CLIENT_HASH_BITS 4 2981da177e4SLinus Torvalds #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) 2991da177e4SLinus Torvalds #define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds #define clientid_hashval(id) \ 3021da177e4SLinus Torvalds ((id) & CLIENT_HASH_MASK) 303a55370a3SNeilBrown #define clientstr_hashval(name) \ 304a55370a3SNeilBrown (opaque_hashval((name), 8) & CLIENT_HASH_MASK) 3051da177e4SLinus Torvalds /* 3061da177e4SLinus Torvalds * reclaim_str_hashtbl[] holds known client info from previous reset/reboot 3071da177e4SLinus Torvalds * used in reboot/reset lease grace period processing 3081da177e4SLinus Torvalds * 3091da177e4SLinus Torvalds * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed 3101da177e4SLinus Torvalds * setclientid_confirmed info. 3111da177e4SLinus Torvalds * 3121da177e4SLinus Torvalds * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed 3131da177e4SLinus Torvalds * setclientid info. 3141da177e4SLinus Torvalds * 3151da177e4SLinus Torvalds * client_lru holds client queue ordered by nfs4_client.cl_time 3161da177e4SLinus Torvalds * for lease renewal. 3171da177e4SLinus Torvalds * 3181da177e4SLinus Torvalds * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time 3191da177e4SLinus Torvalds * for last close replay. 3201da177e4SLinus Torvalds */ 3211da177e4SLinus Torvalds static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE]; 3221da177e4SLinus Torvalds static int reclaim_str_hashtbl_size = 0; 3231da177e4SLinus Torvalds static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; 3241da177e4SLinus Torvalds static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE]; 3251da177e4SLinus Torvalds static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE]; 3261da177e4SLinus Torvalds static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; 3271da177e4SLinus Torvalds static struct list_head client_lru; 3281da177e4SLinus Torvalds static struct list_head close_lru; 3291da177e4SLinus Torvalds 3302283963fSJ. Bruce Fields static void unhash_generic_stateid(struct nfs4_stateid *stp) 3312283963fSJ. Bruce Fields { 3322283963fSJ. Bruce Fields list_del(&stp->st_hash); 3332283963fSJ. Bruce Fields list_del(&stp->st_perfile); 3342283963fSJ. Bruce Fields list_del(&stp->st_perstateowner); 3352283963fSJ. Bruce Fields } 3362283963fSJ. Bruce Fields 3372283963fSJ. Bruce Fields static void free_generic_stateid(struct nfs4_stateid *stp) 3382283963fSJ. Bruce Fields { 3392283963fSJ. Bruce Fields put_nfs4_file(stp->st_file); 3402283963fSJ. Bruce Fields kmem_cache_free(stateid_slab, stp); 3412283963fSJ. Bruce Fields } 3422283963fSJ. Bruce Fields 3432283963fSJ. Bruce Fields static void release_lock_stateid(struct nfs4_stateid *stp) 3442283963fSJ. Bruce Fields { 345f9d7562fSJ. Bruce Fields struct file *file; 346f9d7562fSJ. Bruce Fields 3472283963fSJ. Bruce Fields unhash_generic_stateid(stp); 348f9d7562fSJ. Bruce Fields file = find_any_file(stp->st_file); 349f9d7562fSJ. Bruce Fields if (file) 350f9d7562fSJ. Bruce Fields locks_remove_posix(file, (fl_owner_t)stp->st_stateowner); 3512283963fSJ. Bruce Fields free_generic_stateid(stp); 3522283963fSJ. Bruce Fields } 3532283963fSJ. Bruce Fields 354f044ff83SJ. Bruce Fields static void unhash_lockowner(struct nfs4_stateowner *sop) 355f044ff83SJ. Bruce Fields { 356f044ff83SJ. Bruce Fields struct nfs4_stateid *stp; 357f044ff83SJ. Bruce Fields 358f044ff83SJ. Bruce Fields list_del(&sop->so_idhash); 359f044ff83SJ. Bruce Fields list_del(&sop->so_strhash); 360f044ff83SJ. Bruce Fields list_del(&sop->so_perstateid); 361f044ff83SJ. Bruce Fields while (!list_empty(&sop->so_stateids)) { 362f044ff83SJ. Bruce Fields stp = list_first_entry(&sop->so_stateids, 363f044ff83SJ. Bruce Fields struct nfs4_stateid, st_perstateowner); 364f044ff83SJ. Bruce Fields release_lock_stateid(stp); 365f044ff83SJ. Bruce Fields } 366f044ff83SJ. Bruce Fields } 367f044ff83SJ. Bruce Fields 368f044ff83SJ. Bruce Fields static void release_lockowner(struct nfs4_stateowner *sop) 369f044ff83SJ. Bruce Fields { 370f044ff83SJ. Bruce Fields unhash_lockowner(sop); 371f044ff83SJ. Bruce Fields nfs4_put_stateowner(sop); 372f044ff83SJ. Bruce Fields } 373f044ff83SJ. Bruce Fields 374f1d110caSJ. Bruce Fields static void 375f1d110caSJ. Bruce Fields release_stateid_lockowners(struct nfs4_stateid *open_stp) 376f1d110caSJ. Bruce Fields { 377f1d110caSJ. Bruce Fields struct nfs4_stateowner *lock_sop; 378f1d110caSJ. Bruce Fields 379f1d110caSJ. Bruce Fields while (!list_empty(&open_stp->st_lockowners)) { 380f1d110caSJ. Bruce Fields lock_sop = list_entry(open_stp->st_lockowners.next, 381f1d110caSJ. Bruce Fields struct nfs4_stateowner, so_perstateid); 382f1d110caSJ. Bruce Fields /* list_del(&open_stp->st_lockowners); */ 383f1d110caSJ. Bruce Fields BUG_ON(lock_sop->so_is_open_owner); 384f044ff83SJ. Bruce Fields release_lockowner(lock_sop); 385f1d110caSJ. Bruce Fields } 386f1d110caSJ. Bruce Fields } 387f1d110caSJ. Bruce Fields 388f9d7562fSJ. Bruce Fields /* 389f9d7562fSJ. Bruce Fields * We store the NONE, READ, WRITE, and BOTH bits separately in the 390f9d7562fSJ. Bruce Fields * st_{access,deny}_bmap field of the stateid, in order to track not 391f9d7562fSJ. Bruce Fields * only what share bits are currently in force, but also what 392f9d7562fSJ. Bruce Fields * combinations of share bits previous opens have used. This allows us 393f9d7562fSJ. Bruce Fields * to enforce the recommendation of rfc 3530 14.2.19 that the server 394f9d7562fSJ. Bruce Fields * return an error if the client attempt to downgrade to a combination 395f9d7562fSJ. Bruce Fields * of share bits not explicable by closing some of its previous opens. 396f9d7562fSJ. Bruce Fields * 397f9d7562fSJ. Bruce Fields * XXX: This enforcement is actually incomplete, since we don't keep 398f9d7562fSJ. Bruce Fields * track of access/deny bit combinations; so, e.g., we allow: 399f9d7562fSJ. Bruce Fields * 400f9d7562fSJ. Bruce Fields * OPEN allow read, deny write 401f9d7562fSJ. Bruce Fields * OPEN allow both, deny none 402f9d7562fSJ. Bruce Fields * DOWNGRADE allow read, deny none 403f9d7562fSJ. Bruce Fields * 404f9d7562fSJ. Bruce Fields * which we should reject. 405f9d7562fSJ. Bruce Fields */ 406f9d7562fSJ. Bruce Fields static void 407f9d7562fSJ. Bruce Fields set_access(unsigned int *access, unsigned long bmap) { 408f9d7562fSJ. Bruce Fields int i; 409f9d7562fSJ. Bruce Fields 410f9d7562fSJ. Bruce Fields *access = 0; 411f9d7562fSJ. Bruce Fields for (i = 1; i < 4; i++) { 412f9d7562fSJ. Bruce Fields if (test_bit(i, &bmap)) 413f9d7562fSJ. Bruce Fields *access |= i; 414f9d7562fSJ. Bruce Fields } 415f9d7562fSJ. Bruce Fields } 416f9d7562fSJ. Bruce Fields 417f9d7562fSJ. Bruce Fields static void 418f9d7562fSJ. Bruce Fields set_deny(unsigned int *deny, unsigned long bmap) { 419f9d7562fSJ. Bruce Fields int i; 420f9d7562fSJ. Bruce Fields 421f9d7562fSJ. Bruce Fields *deny = 0; 422f9d7562fSJ. Bruce Fields for (i = 0; i < 4; i++) { 423f9d7562fSJ. Bruce Fields if (test_bit(i, &bmap)) 424f9d7562fSJ. Bruce Fields *deny |= i ; 425f9d7562fSJ. Bruce Fields } 426f9d7562fSJ. Bruce Fields } 427f9d7562fSJ. Bruce Fields 428f9d7562fSJ. Bruce Fields static int 429f9d7562fSJ. Bruce Fields test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { 430f9d7562fSJ. Bruce Fields unsigned int access, deny; 431f9d7562fSJ. Bruce Fields 432f9d7562fSJ. Bruce Fields set_access(&access, stp->st_access_bmap); 433f9d7562fSJ. Bruce Fields set_deny(&deny, stp->st_deny_bmap); 434f9d7562fSJ. Bruce Fields if ((access & open->op_share_deny) || (deny & open->op_share_access)) 435f9d7562fSJ. Bruce Fields return 0; 436f9d7562fSJ. Bruce Fields return 1; 437f9d7562fSJ. Bruce Fields } 438f9d7562fSJ. Bruce Fields 439f9d7562fSJ. Bruce Fields static int nfs4_access_to_omode(u32 access) 440f9d7562fSJ. Bruce Fields { 4418f34a430SJ. Bruce Fields switch (access & NFS4_SHARE_ACCESS_BOTH) { 442f9d7562fSJ. Bruce Fields case NFS4_SHARE_ACCESS_READ: 443f9d7562fSJ. Bruce Fields return O_RDONLY; 444f9d7562fSJ. Bruce Fields case NFS4_SHARE_ACCESS_WRITE: 445f9d7562fSJ. Bruce Fields return O_WRONLY; 446f9d7562fSJ. Bruce Fields case NFS4_SHARE_ACCESS_BOTH: 447f9d7562fSJ. Bruce Fields return O_RDWR; 448f9d7562fSJ. Bruce Fields } 449f9d7562fSJ. Bruce Fields BUG(); 450f9d7562fSJ. Bruce Fields } 451f9d7562fSJ. Bruce Fields 452f9d7562fSJ. Bruce Fields static int nfs4_access_bmap_to_omode(struct nfs4_stateid *stp) 453f9d7562fSJ. Bruce Fields { 454f9d7562fSJ. Bruce Fields unsigned int access; 455f9d7562fSJ. Bruce Fields 456f9d7562fSJ. Bruce Fields set_access(&access, stp->st_access_bmap); 457f9d7562fSJ. Bruce Fields return nfs4_access_to_omode(access); 458f9d7562fSJ. Bruce Fields } 459f9d7562fSJ. Bruce Fields 4602283963fSJ. Bruce Fields static void release_open_stateid(struct nfs4_stateid *stp) 4612283963fSJ. Bruce Fields { 462f9d7562fSJ. Bruce Fields int oflag = nfs4_access_bmap_to_omode(stp); 463f9d7562fSJ. Bruce Fields 4642283963fSJ. Bruce Fields unhash_generic_stateid(stp); 4652283963fSJ. Bruce Fields release_stateid_lockowners(stp); 466f9d7562fSJ. Bruce Fields nfs4_file_put_access(stp->st_file, oflag); 4672283963fSJ. Bruce Fields free_generic_stateid(stp); 4682283963fSJ. Bruce Fields } 4692283963fSJ. Bruce Fields 470f044ff83SJ. Bruce Fields static void unhash_openowner(struct nfs4_stateowner *sop) 471f1d110caSJ. Bruce Fields { 472f1d110caSJ. Bruce Fields struct nfs4_stateid *stp; 473f1d110caSJ. Bruce Fields 474f1d110caSJ. Bruce Fields list_del(&sop->so_idhash); 475f1d110caSJ. Bruce Fields list_del(&sop->so_strhash); 476f1d110caSJ. Bruce Fields list_del(&sop->so_perclient); 477f044ff83SJ. Bruce Fields list_del(&sop->so_perstateid); /* XXX: necessary? */ 478f1d110caSJ. Bruce Fields while (!list_empty(&sop->so_stateids)) { 479f044ff83SJ. Bruce Fields stp = list_first_entry(&sop->so_stateids, 480f1d110caSJ. Bruce Fields struct nfs4_stateid, st_perstateowner); 481f1d110caSJ. Bruce Fields release_open_stateid(stp); 482f1d110caSJ. Bruce Fields } 483f1d110caSJ. Bruce Fields } 484f1d110caSJ. Bruce Fields 485f044ff83SJ. Bruce Fields static void release_openowner(struct nfs4_stateowner *sop) 486f1d110caSJ. Bruce Fields { 487f044ff83SJ. Bruce Fields unhash_openowner(sop); 488f1d110caSJ. Bruce Fields list_del(&sop->so_close_lru); 489f1d110caSJ. Bruce Fields nfs4_put_stateowner(sop); 490f1d110caSJ. Bruce Fields } 491f1d110caSJ. Bruce Fields 4925282fd72SMarc Eshel #define SESSION_HASH_SIZE 512 4935282fd72SMarc Eshel static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; 4945282fd72SMarc Eshel 4955282fd72SMarc Eshel static inline int 4965282fd72SMarc Eshel hash_sessionid(struct nfs4_sessionid *sessionid) 4975282fd72SMarc Eshel { 4985282fd72SMarc Eshel struct nfsd4_sessionid *sid = (struct nfsd4_sessionid *)sessionid; 4995282fd72SMarc Eshel 5005282fd72SMarc Eshel return sid->sequence % SESSION_HASH_SIZE; 5015282fd72SMarc Eshel } 5025282fd72SMarc Eshel 5035282fd72SMarc Eshel static inline void 5045282fd72SMarc Eshel dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid) 5055282fd72SMarc Eshel { 5065282fd72SMarc Eshel u32 *ptr = (u32 *)(&sessionid->data[0]); 5075282fd72SMarc Eshel dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]); 5085282fd72SMarc Eshel } 5095282fd72SMarc Eshel 510ec6b5d7bSAndy Adamson static void 511ec6b5d7bSAndy Adamson gen_sessionid(struct nfsd4_session *ses) 512ec6b5d7bSAndy Adamson { 513ec6b5d7bSAndy Adamson struct nfs4_client *clp = ses->se_client; 514ec6b5d7bSAndy Adamson struct nfsd4_sessionid *sid; 515ec6b5d7bSAndy Adamson 516ec6b5d7bSAndy Adamson sid = (struct nfsd4_sessionid *)ses->se_sessionid.data; 517ec6b5d7bSAndy Adamson sid->clientid = clp->cl_clientid; 518ec6b5d7bSAndy Adamson sid->sequence = current_sessionid++; 519ec6b5d7bSAndy Adamson sid->reserved = 0; 520ec6b5d7bSAndy Adamson } 521ec6b5d7bSAndy Adamson 522ec6b5d7bSAndy Adamson /* 523a649637cSAndy Adamson * The protocol defines ca_maxresponssize_cached to include the size of 524a649637cSAndy Adamson * the rpc header, but all we need to cache is the data starting after 525a649637cSAndy Adamson * the end of the initial SEQUENCE operation--the rest we regenerate 526a649637cSAndy Adamson * each time. Therefore we can advertise a ca_maxresponssize_cached 527a649637cSAndy Adamson * value that is the number of bytes in our cache plus a few additional 528a649637cSAndy Adamson * bytes. In order to stay on the safe side, and not promise more than 529a649637cSAndy Adamson * we can cache, those additional bytes must be the minimum possible: 24 530a649637cSAndy Adamson * bytes of rpc header (xid through accept state, with AUTH_NULL 531a649637cSAndy Adamson * verifier), 12 for the compound header (with zero-length tag), and 44 532a649637cSAndy Adamson * for the SEQUENCE op response: 533ec6b5d7bSAndy Adamson */ 534a649637cSAndy Adamson #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44) 535a649637cSAndy Adamson 536a649637cSAndy Adamson /* 537a649637cSAndy Adamson * Give the client the number of ca_maxresponsesize_cached slots it 538a649637cSAndy Adamson * requests, of size bounded by NFSD_SLOT_CACHE_SIZE, 539a649637cSAndy Adamson * NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more 540a649637cSAndy Adamson * than NFSD_MAX_SLOTS_PER_SESSION. 541a649637cSAndy Adamson * 542a649637cSAndy Adamson * If we run out of reserved DRC memory we should (up to a point) 543a649637cSAndy Adamson * re-negotiate active sessions and reduce their slot usage to make 544a649637cSAndy Adamson * rooom for new connections. For now we just fail the create session. 545a649637cSAndy Adamson */ 546a649637cSAndy Adamson static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan) 547ec6b5d7bSAndy Adamson { 548a649637cSAndy Adamson int mem, size = fchan->maxresp_cached; 549ec6b5d7bSAndy Adamson 5505d77ddfbSAndy Adamson if (fchan->maxreqs < 1) 5515d77ddfbSAndy Adamson return nfserr_inval; 5525d77ddfbSAndy Adamson 553a649637cSAndy Adamson if (size < NFSD_MIN_HDR_SEQ_SZ) 554a649637cSAndy Adamson size = NFSD_MIN_HDR_SEQ_SZ; 555a649637cSAndy Adamson size -= NFSD_MIN_HDR_SEQ_SZ; 556a649637cSAndy Adamson if (size > NFSD_SLOT_CACHE_SIZE) 557a649637cSAndy Adamson size = NFSD_SLOT_CACHE_SIZE; 558a649637cSAndy Adamson 559a649637cSAndy Adamson /* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */ 560a649637cSAndy Adamson mem = fchan->maxreqs * size; 561a649637cSAndy Adamson if (mem > NFSD_MAX_MEM_PER_SESSION) { 562a649637cSAndy Adamson fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size; 563a649637cSAndy Adamson if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION) 564ec6b5d7bSAndy Adamson fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION; 565a649637cSAndy Adamson mem = fchan->maxreqs * size; 566a649637cSAndy Adamson } 567ec6b5d7bSAndy Adamson 5684bd9b0f4SAndy Adamson spin_lock(&nfsd_drc_lock); 569a649637cSAndy Adamson /* bound the total session drc memory ussage */ 570a649637cSAndy Adamson if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) { 571a649637cSAndy Adamson fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size; 572a649637cSAndy Adamson mem = fchan->maxreqs * size; 573a649637cSAndy Adamson } 5740c193054SAndy Adamson nfsd_drc_mem_used += mem; 5754bd9b0f4SAndy Adamson spin_unlock(&nfsd_drc_lock); 576ec6b5d7bSAndy Adamson 577b101ebbcSAndy Adamson if (fchan->maxreqs == 0) 5784731030dSJ. Bruce Fields return nfserr_jukebox; 579ec6b5d7bSAndy Adamson 580a649637cSAndy Adamson fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ; 581b101ebbcSAndy Adamson return 0; 582ec6b5d7bSAndy Adamson } 583ec6b5d7bSAndy Adamson 584ec6b5d7bSAndy Adamson /* 585ec6b5d7bSAndy Adamson * fchan holds the client values on input, and the server values on output 586ddc04fd4SAndy Adamson * sv_max_mesg is the maximum payload plus one page for overhead. 587ec6b5d7bSAndy Adamson */ 588ec6b5d7bSAndy Adamson static int init_forechannel_attrs(struct svc_rqst *rqstp, 5896c18ba9fSAlexandros Batsakis struct nfsd4_channel_attrs *session_fchan, 590ec6b5d7bSAndy Adamson struct nfsd4_channel_attrs *fchan) 591ec6b5d7bSAndy Adamson { 592ec6b5d7bSAndy Adamson int status = 0; 593ddc04fd4SAndy Adamson __u32 maxcount = nfsd_serv->sv_max_mesg; 594ec6b5d7bSAndy Adamson 595ec6b5d7bSAndy Adamson /* headerpadsz set to zero in encode routine */ 596ec6b5d7bSAndy Adamson 597ec6b5d7bSAndy Adamson /* Use the client's max request and max response size if possible */ 598ec6b5d7bSAndy Adamson if (fchan->maxreq_sz > maxcount) 599ec6b5d7bSAndy Adamson fchan->maxreq_sz = maxcount; 6006c18ba9fSAlexandros Batsakis session_fchan->maxreq_sz = fchan->maxreq_sz; 601ec6b5d7bSAndy Adamson 602ec6b5d7bSAndy Adamson if (fchan->maxresp_sz > maxcount) 603ec6b5d7bSAndy Adamson fchan->maxresp_sz = maxcount; 6046c18ba9fSAlexandros Batsakis session_fchan->maxresp_sz = fchan->maxresp_sz; 605ec6b5d7bSAndy Adamson 606ec6b5d7bSAndy Adamson /* Use the client's maxops if possible */ 607ec6b5d7bSAndy Adamson if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND) 608ec6b5d7bSAndy Adamson fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND; 6096c18ba9fSAlexandros Batsakis session_fchan->maxops = fchan->maxops; 610ec6b5d7bSAndy Adamson 611ec6b5d7bSAndy Adamson /* FIXME: Error means no more DRC pages so the server should 612ec6b5d7bSAndy Adamson * recover pages from existing sessions. For now fail session 613ec6b5d7bSAndy Adamson * creation. 614ec6b5d7bSAndy Adamson */ 615a649637cSAndy Adamson status = set_forechannel_drc_size(fchan); 616ec6b5d7bSAndy Adamson 617a649637cSAndy Adamson session_fchan->maxresp_cached = fchan->maxresp_cached; 6186c18ba9fSAlexandros Batsakis session_fchan->maxreqs = fchan->maxreqs; 619a649637cSAndy Adamson 620a649637cSAndy Adamson dprintk("%s status %d\n", __func__, status); 621ec6b5d7bSAndy Adamson return status; 622ec6b5d7bSAndy Adamson } 623ec6b5d7bSAndy Adamson 624557ce264SAndy Adamson static void 625557ce264SAndy Adamson free_session_slots(struct nfsd4_session *ses) 626557ce264SAndy Adamson { 627557ce264SAndy Adamson int i; 628557ce264SAndy Adamson 629557ce264SAndy Adamson for (i = 0; i < ses->se_fchannel.maxreqs; i++) 630557ce264SAndy Adamson kfree(ses->se_slots[i]); 631557ce264SAndy Adamson } 632557ce264SAndy Adamson 633efe0cb6dSJ. Bruce Fields /* 634efe0cb6dSJ. Bruce Fields * We don't actually need to cache the rpc and session headers, so we 635efe0cb6dSJ. Bruce Fields * can allocate a little less for each slot: 636efe0cb6dSJ. Bruce Fields */ 637efe0cb6dSJ. Bruce Fields static inline int slot_bytes(struct nfsd4_channel_attrs *ca) 638efe0cb6dSJ. Bruce Fields { 639efe0cb6dSJ. Bruce Fields return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ; 640efe0cb6dSJ. Bruce Fields } 641efe0cb6dSJ. Bruce Fields 642ec6b5d7bSAndy Adamson static int 643ec6b5d7bSAndy Adamson alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, 644ec6b5d7bSAndy Adamson struct nfsd4_create_session *cses) 645ec6b5d7bSAndy Adamson { 646ec6b5d7bSAndy Adamson struct nfsd4_session *new, tmp; 647557ce264SAndy Adamson struct nfsd4_slot *sp; 648557ce264SAndy Adamson int idx, slotsize, cachesize, i; 649557ce264SAndy Adamson int status; 650ec6b5d7bSAndy Adamson 651ec6b5d7bSAndy Adamson memset(&tmp, 0, sizeof(tmp)); 652ec6b5d7bSAndy Adamson 653ec6b5d7bSAndy Adamson /* FIXME: For now, we just accept the client back channel attributes. */ 6546c18ba9fSAlexandros Batsakis tmp.se_bchannel = cses->back_channel; 6556c18ba9fSAlexandros Batsakis status = init_forechannel_attrs(rqstp, &tmp.se_fchannel, 6566c18ba9fSAlexandros Batsakis &cses->fore_channel); 657ec6b5d7bSAndy Adamson if (status) 658ec6b5d7bSAndy Adamson goto out; 659ec6b5d7bSAndy Adamson 660557ce264SAndy Adamson BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot) 661557ce264SAndy Adamson + sizeof(struct nfsd4_session) > PAGE_SIZE); 662557ce264SAndy Adamson 6634731030dSJ. Bruce Fields status = nfserr_jukebox; 664557ce264SAndy Adamson /* allocate struct nfsd4_session and slot table pointers in one piece */ 665557ce264SAndy Adamson slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *); 666ec6b5d7bSAndy Adamson new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL); 667ec6b5d7bSAndy Adamson if (!new) 668ec6b5d7bSAndy Adamson goto out; 669ec6b5d7bSAndy Adamson 670ec6b5d7bSAndy Adamson memcpy(new, &tmp, sizeof(*new)); 671ec6b5d7bSAndy Adamson 672557ce264SAndy Adamson /* allocate each struct nfsd4_slot and data cache in one piece */ 673efe0cb6dSJ. Bruce Fields cachesize = slot_bytes(&new->se_fchannel); 674557ce264SAndy Adamson for (i = 0; i < new->se_fchannel.maxreqs; i++) { 675557ce264SAndy Adamson sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL); 676557ce264SAndy Adamson if (!sp) 677557ce264SAndy Adamson goto out_free; 678557ce264SAndy Adamson new->se_slots[i] = sp; 679557ce264SAndy Adamson } 680557ce264SAndy Adamson 681ec6b5d7bSAndy Adamson new->se_client = clp; 682ec6b5d7bSAndy Adamson gen_sessionid(new); 683ec6b5d7bSAndy Adamson idx = hash_sessionid(&new->se_sessionid); 684ec6b5d7bSAndy Adamson memcpy(clp->cl_sessionid.data, new->se_sessionid.data, 685ec6b5d7bSAndy Adamson NFS4_MAX_SESSIONID_LEN); 686ec6b5d7bSAndy Adamson 687ec6b5d7bSAndy Adamson new->se_flags = cses->flags; 688ec6b5d7bSAndy Adamson kref_init(&new->se_ref); 6899089f1b4SBenny Halevy spin_lock(&client_lock); 690ec6b5d7bSAndy Adamson list_add(&new->se_hash, &sessionid_hashtbl[idx]); 691ec6b5d7bSAndy Adamson list_add(&new->se_perclnt, &clp->cl_sessions); 6929089f1b4SBenny Halevy spin_unlock(&client_lock); 693ec6b5d7bSAndy Adamson 694ec6b5d7bSAndy Adamson status = nfs_ok; 695ec6b5d7bSAndy Adamson out: 696ec6b5d7bSAndy Adamson return status; 697557ce264SAndy Adamson out_free: 698557ce264SAndy Adamson free_session_slots(new); 699557ce264SAndy Adamson kfree(new); 700557ce264SAndy Adamson goto out; 701ec6b5d7bSAndy Adamson } 702ec6b5d7bSAndy Adamson 7039089f1b4SBenny Halevy /* caller must hold client_lock */ 7045282fd72SMarc Eshel static struct nfsd4_session * 7055282fd72SMarc Eshel find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) 7065282fd72SMarc Eshel { 7075282fd72SMarc Eshel struct nfsd4_session *elem; 7085282fd72SMarc Eshel int idx; 7095282fd72SMarc Eshel 7105282fd72SMarc Eshel dump_sessionid(__func__, sessionid); 7115282fd72SMarc Eshel idx = hash_sessionid(sessionid); 7125282fd72SMarc Eshel /* Search in the appropriate list */ 7135282fd72SMarc Eshel list_for_each_entry(elem, &sessionid_hashtbl[idx], se_hash) { 7145282fd72SMarc Eshel if (!memcmp(elem->se_sessionid.data, sessionid->data, 7155282fd72SMarc Eshel NFS4_MAX_SESSIONID_LEN)) { 7165282fd72SMarc Eshel return elem; 7175282fd72SMarc Eshel } 7185282fd72SMarc Eshel } 7195282fd72SMarc Eshel 7205282fd72SMarc Eshel dprintk("%s: session not found\n", __func__); 7215282fd72SMarc Eshel return NULL; 7225282fd72SMarc Eshel } 7235282fd72SMarc Eshel 7249089f1b4SBenny Halevy /* caller must hold client_lock */ 7257116ed6bSAndy Adamson static void 7265282fd72SMarc Eshel unhash_session(struct nfsd4_session *ses) 7277116ed6bSAndy Adamson { 7287116ed6bSAndy Adamson list_del(&ses->se_hash); 7297116ed6bSAndy Adamson list_del(&ses->se_perclnt); 7305282fd72SMarc Eshel } 7315282fd72SMarc Eshel 7327116ed6bSAndy Adamson void 7337116ed6bSAndy Adamson free_session(struct kref *kref) 7347116ed6bSAndy Adamson { 7357116ed6bSAndy Adamson struct nfsd4_session *ses; 736dd829c45SJ. Bruce Fields int mem; 7377116ed6bSAndy Adamson 7387116ed6bSAndy Adamson ses = container_of(kref, struct nfsd4_session, se_ref); 739be98d1bbSAndy Adamson spin_lock(&nfsd_drc_lock); 740efe0cb6dSJ. Bruce Fields mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel); 741dd829c45SJ. Bruce Fields nfsd_drc_mem_used -= mem; 742be98d1bbSAndy Adamson spin_unlock(&nfsd_drc_lock); 743557ce264SAndy Adamson free_session_slots(ses); 7447116ed6bSAndy Adamson kfree(ses); 7457116ed6bSAndy Adamson } 7467116ed6bSAndy Adamson 74736acb66bSBenny Halevy /* must be called under the client_lock */ 7481da177e4SLinus Torvalds static inline void 74936acb66bSBenny Halevy renew_client_locked(struct nfs4_client *clp) 7501da177e4SLinus Torvalds { 75107cd4909SBenny Halevy if (is_client_expired(clp)) { 75207cd4909SBenny Halevy dprintk("%s: client (clientid %08x/%08x) already expired\n", 75307cd4909SBenny Halevy __func__, 75407cd4909SBenny Halevy clp->cl_clientid.cl_boot, 75507cd4909SBenny Halevy clp->cl_clientid.cl_id); 75607cd4909SBenny Halevy return; 75707cd4909SBenny Halevy } 75807cd4909SBenny Halevy 7591da177e4SLinus Torvalds /* 7601da177e4SLinus Torvalds * Move client to the end to the LRU list. 7611da177e4SLinus Torvalds */ 7621da177e4SLinus Torvalds dprintk("renewing client (clientid %08x/%08x)\n", 7631da177e4SLinus Torvalds clp->cl_clientid.cl_boot, 7641da177e4SLinus Torvalds clp->cl_clientid.cl_id); 7651da177e4SLinus Torvalds list_move_tail(&clp->cl_lru, &client_lru); 7661da177e4SLinus Torvalds clp->cl_time = get_seconds(); 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 76936acb66bSBenny Halevy static inline void 77036acb66bSBenny Halevy renew_client(struct nfs4_client *clp) 77136acb66bSBenny Halevy { 77236acb66bSBenny Halevy spin_lock(&client_lock); 77336acb66bSBenny Halevy renew_client_locked(clp); 77436acb66bSBenny Halevy spin_unlock(&client_lock); 77536acb66bSBenny Halevy } 77636acb66bSBenny Halevy 7771da177e4SLinus Torvalds /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ 7781da177e4SLinus Torvalds static int 7791da177e4SLinus Torvalds STALE_CLIENTID(clientid_t *clid) 7801da177e4SLinus Torvalds { 7811da177e4SLinus Torvalds if (clid->cl_boot == boot_time) 7821da177e4SLinus Torvalds return 0; 78360adfc50SAndy Adamson dprintk("NFSD stale clientid (%08x/%08x) boot_time %08lx\n", 78460adfc50SAndy Adamson clid->cl_boot, clid->cl_id, boot_time); 7851da177e4SLinus Torvalds return 1; 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds /* 7891da177e4SLinus Torvalds * XXX Should we use a slab cache ? 7901da177e4SLinus Torvalds * This type of memory management is somewhat inefficient, but we use it 7911da177e4SLinus Torvalds * anyway since SETCLIENTID is not a common operation. 7921da177e4SLinus Torvalds */ 79335bba9a3SJ. Bruce Fields static struct nfs4_client *alloc_client(struct xdr_netobj name) 7941da177e4SLinus Torvalds { 7951da177e4SLinus Torvalds struct nfs4_client *clp; 7961da177e4SLinus Torvalds 79735bba9a3SJ. Bruce Fields clp = kzalloc(sizeof(struct nfs4_client), GFP_KERNEL); 79835bba9a3SJ. Bruce Fields if (clp == NULL) 79935bba9a3SJ. Bruce Fields return NULL; 80035bba9a3SJ. Bruce Fields clp->cl_name.data = kmalloc(name.len, GFP_KERNEL); 80135bba9a3SJ. Bruce Fields if (clp->cl_name.data == NULL) { 80235bba9a3SJ. Bruce Fields kfree(clp); 80335bba9a3SJ. Bruce Fields return NULL; 80435bba9a3SJ. Bruce Fields } 8051da177e4SLinus Torvalds memcpy(clp->cl_name.data, name.data, name.len); 8061da177e4SLinus Torvalds clp->cl_name.len = name.len; 8071da177e4SLinus Torvalds return clp; 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds static inline void 8111da177e4SLinus Torvalds free_client(struct nfs4_client *clp) 8121da177e4SLinus Torvalds { 8131da177e4SLinus Torvalds if (clp->cl_cred.cr_group_info) 8141da177e4SLinus Torvalds put_group_info(clp->cl_cred.cr_group_info); 81568e76ad0SOlga Kornievskaia kfree(clp->cl_principal); 8161da177e4SLinus Torvalds kfree(clp->cl_name.data); 8171da177e4SLinus Torvalds kfree(clp); 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds 820d7682988SBenny Halevy void 821d7682988SBenny Halevy release_session_client(struct nfsd4_session *session) 822d7682988SBenny Halevy { 823d7682988SBenny Halevy struct nfs4_client *clp = session->se_client; 824d7682988SBenny Halevy 825d7682988SBenny Halevy if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock)) 826d7682988SBenny Halevy return; 827d7682988SBenny Halevy if (is_client_expired(clp)) { 828d7682988SBenny Halevy free_client(clp); 829d7682988SBenny Halevy session->se_client = NULL; 830d7682988SBenny Halevy } else 831d7682988SBenny Halevy renew_client_locked(clp); 832d7682988SBenny Halevy spin_unlock(&client_lock); 833d7682988SBenny Halevy } 834d7682988SBenny Halevy 83584d38ac9SBenny Halevy /* must be called under the client_lock */ 83684d38ac9SBenny Halevy static inline void 83784d38ac9SBenny Halevy unhash_client_locked(struct nfs4_client *clp) 83884d38ac9SBenny Halevy { 83907cd4909SBenny Halevy mark_client_expired(clp); 84084d38ac9SBenny Halevy list_del(&clp->cl_lru); 84184d38ac9SBenny Halevy while (!list_empty(&clp->cl_sessions)) { 84284d38ac9SBenny Halevy struct nfsd4_session *ses; 84384d38ac9SBenny Halevy ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, 84484d38ac9SBenny Halevy se_perclnt); 84584d38ac9SBenny Halevy unhash_session(ses); 84684d38ac9SBenny Halevy nfsd4_put_session(ses); 84784d38ac9SBenny Halevy } 84884d38ac9SBenny Halevy } 84984d38ac9SBenny Halevy 8501da177e4SLinus Torvalds static void 8511da177e4SLinus Torvalds expire_client(struct nfs4_client *clp) 8521da177e4SLinus Torvalds { 8531da177e4SLinus Torvalds struct nfs4_stateowner *sop; 8541da177e4SLinus Torvalds struct nfs4_delegation *dp; 8551da177e4SLinus Torvalds struct list_head reaplist; 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds INIT_LIST_HEAD(&reaplist); 8581da177e4SLinus Torvalds spin_lock(&recall_lock); 859ea1da636SNeilBrown while (!list_empty(&clp->cl_delegations)) { 860ea1da636SNeilBrown dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); 8611da177e4SLinus Torvalds dprintk("NFSD: expire client. dp %p, fp %p\n", dp, 8621da177e4SLinus Torvalds dp->dl_flock); 863ea1da636SNeilBrown list_del_init(&dp->dl_perclnt); 8641da177e4SLinus Torvalds list_move(&dp->dl_recall_lru, &reaplist); 8651da177e4SLinus Torvalds } 8661da177e4SLinus Torvalds spin_unlock(&recall_lock); 8671da177e4SLinus Torvalds while (!list_empty(&reaplist)) { 8681da177e4SLinus Torvalds dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); 8691da177e4SLinus Torvalds list_del_init(&dp->dl_recall_lru); 8701da177e4SLinus Torvalds unhash_delegation(dp); 8711da177e4SLinus Torvalds } 872ea1da636SNeilBrown while (!list_empty(&clp->cl_openowners)) { 873ea1da636SNeilBrown sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); 874f044ff83SJ. Bruce Fields release_openowner(sop); 8751da177e4SLinus Torvalds } 876*6ff8da08SJ. Bruce Fields nfsd4_shutdown_callback(clp); 8772bf23875SJ. Bruce Fields if (clp->cl_cb_conn.cb_xprt) 8782bf23875SJ. Bruce Fields svc_xprt_put(clp->cl_cb_conn.cb_xprt); 87984d38ac9SBenny Halevy list_del(&clp->cl_idhash); 88084d38ac9SBenny Halevy list_del(&clp->cl_strhash); 88184d38ac9SBenny Halevy spin_lock(&client_lock); 88284d38ac9SBenny Halevy unhash_client_locked(clp); 88346583e25SBenny Halevy if (atomic_read(&clp->cl_refcount) == 0) 884b12a05cbSJ. Bruce Fields free_client(clp); 88546583e25SBenny Halevy spin_unlock(&client_lock); 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds 88835bba9a3SJ. Bruce Fields static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) 88935bba9a3SJ. Bruce Fields { 89035bba9a3SJ. Bruce Fields memcpy(target->cl_verifier.data, source->data, 89135bba9a3SJ. Bruce Fields sizeof(target->cl_verifier.data)); 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds 89435bba9a3SJ. Bruce Fields static void copy_clid(struct nfs4_client *target, struct nfs4_client *source) 89535bba9a3SJ. Bruce Fields { 8961da177e4SLinus Torvalds target->cl_clientid.cl_boot = source->cl_clientid.cl_boot; 8971da177e4SLinus Torvalds target->cl_clientid.cl_id = source->cl_clientid.cl_id; 8981da177e4SLinus Torvalds } 8991da177e4SLinus Torvalds 90035bba9a3SJ. Bruce Fields static void copy_cred(struct svc_cred *target, struct svc_cred *source) 90135bba9a3SJ. Bruce Fields { 9021da177e4SLinus Torvalds target->cr_uid = source->cr_uid; 9031da177e4SLinus Torvalds target->cr_gid = source->cr_gid; 9041da177e4SLinus Torvalds target->cr_group_info = source->cr_group_info; 9051da177e4SLinus Torvalds get_group_info(target->cr_group_info); 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds 90835bba9a3SJ. Bruce Fields static int same_name(const char *n1, const char *n2) 909599e0a22SJ. Bruce Fields { 910a55370a3SNeilBrown return 0 == memcmp(n1, n2, HEXDIR_LEN); 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds static int 914599e0a22SJ. Bruce Fields same_verf(nfs4_verifier *v1, nfs4_verifier *v2) 915599e0a22SJ. Bruce Fields { 916599e0a22SJ. Bruce Fields return 0 == memcmp(v1->data, v2->data, sizeof(v1->data)); 9171da177e4SLinus Torvalds } 9181da177e4SLinus Torvalds 9191da177e4SLinus Torvalds static int 920599e0a22SJ. Bruce Fields same_clid(clientid_t *cl1, clientid_t *cl2) 921599e0a22SJ. Bruce Fields { 922599e0a22SJ. Bruce Fields return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id); 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds /* XXX what about NGROUP */ 9261da177e4SLinus Torvalds static int 927599e0a22SJ. Bruce Fields same_creds(struct svc_cred *cr1, struct svc_cred *cr2) 928599e0a22SJ. Bruce Fields { 929599e0a22SJ. Bruce Fields return cr1->cr_uid == cr2->cr_uid; 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds 9325ec7b46cSJ. Bruce Fields static void gen_clid(struct nfs4_client *clp) 9335ec7b46cSJ. Bruce Fields { 9345ec7b46cSJ. Bruce Fields static u32 current_clientid = 1; 9355ec7b46cSJ. Bruce Fields 9361da177e4SLinus Torvalds clp->cl_clientid.cl_boot = boot_time; 9371da177e4SLinus Torvalds clp->cl_clientid.cl_id = current_clientid++; 9381da177e4SLinus Torvalds } 9391da177e4SLinus Torvalds 940deda2faaSJ. Bruce Fields static void gen_confirm(struct nfs4_client *clp) 941deda2faaSJ. Bruce Fields { 942deda2faaSJ. Bruce Fields static u32 i; 9431da177e4SLinus Torvalds u32 *p; 9441da177e4SLinus Torvalds 9451da177e4SLinus Torvalds p = (u32 *)clp->cl_confirm.data; 946deda2faaSJ. Bruce Fields *p++ = get_seconds(); 947deda2faaSJ. Bruce Fields *p++ = i++; 9481da177e4SLinus Torvalds } 9491da177e4SLinus Torvalds 950b09333c4SRicardo Labiaga static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, 951b09333c4SRicardo Labiaga struct svc_rqst *rqstp, nfs4_verifier *verf) 952b09333c4SRicardo Labiaga { 953b09333c4SRicardo Labiaga struct nfs4_client *clp; 954b09333c4SRicardo Labiaga struct sockaddr *sa = svc_addr(rqstp); 955b09333c4SRicardo Labiaga char *princ; 956b09333c4SRicardo Labiaga 957b09333c4SRicardo Labiaga clp = alloc_client(name); 958b09333c4SRicardo Labiaga if (clp == NULL) 959b09333c4SRicardo Labiaga return NULL; 960b09333c4SRicardo Labiaga 961b09333c4SRicardo Labiaga princ = svc_gss_principal(rqstp); 962b09333c4SRicardo Labiaga if (princ) { 963b09333c4SRicardo Labiaga clp->cl_principal = kstrdup(princ, GFP_KERNEL); 964b09333c4SRicardo Labiaga if (clp->cl_principal == NULL) { 965b09333c4SRicardo Labiaga free_client(clp); 966b09333c4SRicardo Labiaga return NULL; 967b09333c4SRicardo Labiaga } 968b09333c4SRicardo Labiaga } 969b09333c4SRicardo Labiaga 970b09333c4SRicardo Labiaga memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); 97146583e25SBenny Halevy atomic_set(&clp->cl_refcount, 0); 9722bf23875SJ. Bruce Fields atomic_set(&clp->cl_cb_set, 0); 973b09333c4SRicardo Labiaga INIT_LIST_HEAD(&clp->cl_idhash); 974b09333c4SRicardo Labiaga INIT_LIST_HEAD(&clp->cl_strhash); 975b09333c4SRicardo Labiaga INIT_LIST_HEAD(&clp->cl_openowners); 976b09333c4SRicardo Labiaga INIT_LIST_HEAD(&clp->cl_delegations); 977b09333c4SRicardo Labiaga INIT_LIST_HEAD(&clp->cl_sessions); 978b09333c4SRicardo Labiaga INIT_LIST_HEAD(&clp->cl_lru); 979*6ff8da08SJ. Bruce Fields spin_lock_init(&clp->cl_lock); 980cee277d9SJ. Bruce Fields INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); 98107cd4909SBenny Halevy clp->cl_time = get_seconds(); 982b09333c4SRicardo Labiaga clear_bit(0, &clp->cl_cb_slot_busy); 983b09333c4SRicardo Labiaga rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); 984b09333c4SRicardo Labiaga copy_verf(clp, verf); 985b09333c4SRicardo Labiaga rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa); 986b09333c4SRicardo Labiaga clp->cl_flavor = rqstp->rq_flavor; 987b09333c4SRicardo Labiaga copy_cred(&clp->cl_cred, &rqstp->rq_cred); 988b09333c4SRicardo Labiaga gen_confirm(clp); 989b09333c4SRicardo Labiaga 990b09333c4SRicardo Labiaga return clp; 991b09333c4SRicardo Labiaga } 992b09333c4SRicardo Labiaga 99335bba9a3SJ. Bruce Fields static int check_name(struct xdr_netobj name) 99435bba9a3SJ. Bruce Fields { 9951da177e4SLinus Torvalds if (name.len == 0) 9961da177e4SLinus Torvalds return 0; 9971da177e4SLinus Torvalds if (name.len > NFS4_OPAQUE_LIMIT) { 9982fdada03SJ. Bruce Fields dprintk("NFSD: check_name: name too long(%d)!\n", name.len); 9991da177e4SLinus Torvalds return 0; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds return 1; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 1004fd39ca9aSNeilBrown static void 10051da177e4SLinus Torvalds add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) 10061da177e4SLinus Torvalds { 10071da177e4SLinus Torvalds unsigned int idhashval; 10081da177e4SLinus Torvalds 10091da177e4SLinus Torvalds list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); 10101da177e4SLinus Torvalds idhashval = clientid_hashval(clp->cl_clientid.cl_id); 10111da177e4SLinus Torvalds list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); 101236acb66bSBenny Halevy renew_client(clp); 10131da177e4SLinus Torvalds } 10141da177e4SLinus Torvalds 1015fd39ca9aSNeilBrown static void 10161da177e4SLinus Torvalds move_to_confirmed(struct nfs4_client *clp) 10171da177e4SLinus Torvalds { 10181da177e4SLinus Torvalds unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); 10191da177e4SLinus Torvalds unsigned int strhashval; 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); 1022f116629dSAkinobu Mita list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); 1023a55370a3SNeilBrown strhashval = clientstr_hashval(clp->cl_recdir); 1024328efbabSBenny Halevy list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); 10251da177e4SLinus Torvalds renew_client(clp); 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds static struct nfs4_client * 10291da177e4SLinus Torvalds find_confirmed_client(clientid_t *clid) 10301da177e4SLinus Torvalds { 10311da177e4SLinus Torvalds struct nfs4_client *clp; 10321da177e4SLinus Torvalds unsigned int idhashval = clientid_hashval(clid->cl_id); 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { 1035599e0a22SJ. Bruce Fields if (same_clid(&clp->cl_clientid, clid)) 10361da177e4SLinus Torvalds return clp; 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds return NULL; 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds static struct nfs4_client * 10421da177e4SLinus Torvalds find_unconfirmed_client(clientid_t *clid) 10431da177e4SLinus Torvalds { 10441da177e4SLinus Torvalds struct nfs4_client *clp; 10451da177e4SLinus Torvalds unsigned int idhashval = clientid_hashval(clid->cl_id); 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { 1048599e0a22SJ. Bruce Fields if (same_clid(&clp->cl_clientid, clid)) 10491da177e4SLinus Torvalds return clp; 10501da177e4SLinus Torvalds } 10511da177e4SLinus Torvalds return NULL; 10521da177e4SLinus Torvalds } 10531da177e4SLinus Torvalds 1054a1bcecd2SAndy Adamson /* 1055a1bcecd2SAndy Adamson * Return 1 iff clp's clientid establishment method matches the use_exchange_id 1056a1bcecd2SAndy Adamson * parameter. Matching is based on the fact the at least one of the 1057a1bcecd2SAndy Adamson * EXCHGID4_FLAG_USE_{NON_PNFS,PNFS_MDS,PNFS_DS} flags must be set for v4.1 1058a1bcecd2SAndy Adamson * 1059a1bcecd2SAndy Adamson * FIXME: we need to unify the clientid namespaces for nfsv4.x 1060a1bcecd2SAndy Adamson * and correctly deal with client upgrade/downgrade in EXCHANGE_ID 1061a1bcecd2SAndy Adamson * and SET_CLIENTID{,_CONFIRM} 1062a1bcecd2SAndy Adamson */ 1063a1bcecd2SAndy Adamson static inline int 1064a1bcecd2SAndy Adamson match_clientid_establishment(struct nfs4_client *clp, bool use_exchange_id) 1065a1bcecd2SAndy Adamson { 1066a1bcecd2SAndy Adamson bool has_exchange_flags = (clp->cl_exchange_flags != 0); 1067a1bcecd2SAndy Adamson return use_exchange_id == has_exchange_flags; 1068a1bcecd2SAndy Adamson } 1069a1bcecd2SAndy Adamson 107028ce6054SNeilBrown static struct nfs4_client * 1071a1bcecd2SAndy Adamson find_confirmed_client_by_str(const char *dname, unsigned int hashval, 1072a1bcecd2SAndy Adamson bool use_exchange_id) 107328ce6054SNeilBrown { 107428ce6054SNeilBrown struct nfs4_client *clp; 107528ce6054SNeilBrown 107628ce6054SNeilBrown list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { 1077a1bcecd2SAndy Adamson if (same_name(clp->cl_recdir, dname) && 1078a1bcecd2SAndy Adamson match_clientid_establishment(clp, use_exchange_id)) 107928ce6054SNeilBrown return clp; 108028ce6054SNeilBrown } 108128ce6054SNeilBrown return NULL; 108228ce6054SNeilBrown } 108328ce6054SNeilBrown 108428ce6054SNeilBrown static struct nfs4_client * 1085a1bcecd2SAndy Adamson find_unconfirmed_client_by_str(const char *dname, unsigned int hashval, 1086a1bcecd2SAndy Adamson bool use_exchange_id) 108728ce6054SNeilBrown { 108828ce6054SNeilBrown struct nfs4_client *clp; 108928ce6054SNeilBrown 109028ce6054SNeilBrown list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { 1091a1bcecd2SAndy Adamson if (same_name(clp->cl_recdir, dname) && 1092a1bcecd2SAndy Adamson match_clientid_establishment(clp, use_exchange_id)) 109328ce6054SNeilBrown return clp; 109428ce6054SNeilBrown } 109528ce6054SNeilBrown return NULL; 109628ce6054SNeilBrown } 109728ce6054SNeilBrown 1098fd39ca9aSNeilBrown static void 1099fbf4665fSJeff Layton gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid) 11001da177e4SLinus Torvalds { 110107263f1eSJ. Bruce Fields struct nfs4_cb_conn *conn = &clp->cl_cb_conn; 11027077ecbaSJeff Layton unsigned short expected_family; 11031da177e4SLinus Torvalds 11047077ecbaSJeff Layton /* Currently, we only support tcp and tcp6 for the callback channel */ 11057077ecbaSJeff Layton if (se->se_callback_netid_len == 3 && 11067077ecbaSJeff Layton !memcmp(se->se_callback_netid_val, "tcp", 3)) 11077077ecbaSJeff Layton expected_family = AF_INET; 11087077ecbaSJeff Layton else if (se->se_callback_netid_len == 4 && 11097077ecbaSJeff Layton !memcmp(se->se_callback_netid_val, "tcp6", 4)) 11107077ecbaSJeff Layton expected_family = AF_INET6; 11117077ecbaSJeff Layton else 11121da177e4SLinus Torvalds goto out_err; 11131da177e4SLinus Torvalds 111407263f1eSJ. Bruce Fields conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val, 1115aa9a4ec7SJeff Layton se->se_callback_addr_len, 111607263f1eSJ. Bruce Fields (struct sockaddr *)&conn->cb_addr, 111707263f1eSJ. Bruce Fields sizeof(conn->cb_addr)); 1118aa9a4ec7SJeff Layton 111907263f1eSJ. Bruce Fields if (!conn->cb_addrlen || conn->cb_addr.ss_family != expected_family) 11201da177e4SLinus Torvalds goto out_err; 1121aa9a4ec7SJeff Layton 112207263f1eSJ. Bruce Fields if (conn->cb_addr.ss_family == AF_INET6) 112307263f1eSJ. Bruce Fields ((struct sockaddr_in6 *)&conn->cb_addr)->sin6_scope_id = scopeid; 1124fbf4665fSJeff Layton 112507263f1eSJ. Bruce Fields conn->cb_minorversion = 0; 112607263f1eSJ. Bruce Fields conn->cb_prog = se->se_callback_prog; 112707263f1eSJ. Bruce Fields conn->cb_ident = se->se_callback_ident; 11281da177e4SLinus Torvalds return; 11291da177e4SLinus Torvalds out_err: 113007263f1eSJ. Bruce Fields conn->cb_addr.ss_family = AF_UNSPEC; 113107263f1eSJ. Bruce Fields conn->cb_addrlen = 0; 1132849823c5SNeil Brown dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) " 11331da177e4SLinus Torvalds "will not receive delegations\n", 11341da177e4SLinus Torvalds clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds return; 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds 1139074fe897SAndy Adamson /* 1140557ce264SAndy Adamson * Cache a reply. nfsd4_check_drc_limit() has bounded the cache size. 1141074fe897SAndy Adamson */ 1142074fe897SAndy Adamson void 1143074fe897SAndy Adamson nfsd4_store_cache_entry(struct nfsd4_compoundres *resp) 1144074fe897SAndy Adamson { 1145557ce264SAndy Adamson struct nfsd4_slot *slot = resp->cstate.slot; 1146557ce264SAndy Adamson unsigned int base; 1147074fe897SAndy Adamson 1148557ce264SAndy Adamson dprintk("--> %s slot %p\n", __func__, slot); 1149074fe897SAndy Adamson 1150557ce264SAndy Adamson slot->sl_opcnt = resp->opcnt; 1151557ce264SAndy Adamson slot->sl_status = resp->cstate.status; 1152bf864a31SAndy Adamson 1153bf864a31SAndy Adamson if (nfsd4_not_cached(resp)) { 1154557ce264SAndy Adamson slot->sl_datalen = 0; 1155bf864a31SAndy Adamson return; 1156bf864a31SAndy Adamson } 1157557ce264SAndy Adamson slot->sl_datalen = (char *)resp->p - (char *)resp->cstate.datap; 1158557ce264SAndy Adamson base = (char *)resp->cstate.datap - 1159557ce264SAndy Adamson (char *)resp->xbuf->head[0].iov_base; 1160557ce264SAndy Adamson if (read_bytes_from_xdr_buf(resp->xbuf, base, slot->sl_data, 1161557ce264SAndy Adamson slot->sl_datalen)) 1162557ce264SAndy Adamson WARN("%s: sessions DRC could not cache compound\n", __func__); 1163557ce264SAndy Adamson return; 1164074fe897SAndy Adamson } 1165074fe897SAndy Adamson 1166074fe897SAndy Adamson /* 1167abfabf8cSAndy Adamson * Encode the replay sequence operation from the slot values. 1168abfabf8cSAndy Adamson * If cachethis is FALSE encode the uncached rep error on the next 1169abfabf8cSAndy Adamson * operation which sets resp->p and increments resp->opcnt for 1170abfabf8cSAndy Adamson * nfs4svc_encode_compoundres. 1171abfabf8cSAndy Adamson * 1172074fe897SAndy Adamson */ 1173abfabf8cSAndy Adamson static __be32 1174abfabf8cSAndy Adamson nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args, 1175abfabf8cSAndy Adamson struct nfsd4_compoundres *resp) 1176074fe897SAndy Adamson { 1177abfabf8cSAndy Adamson struct nfsd4_op *op; 1178abfabf8cSAndy Adamson struct nfsd4_slot *slot = resp->cstate.slot; 1179074fe897SAndy Adamson 1180abfabf8cSAndy Adamson dprintk("--> %s resp->opcnt %d cachethis %u \n", __func__, 1181557ce264SAndy Adamson resp->opcnt, resp->cstate.slot->sl_cachethis); 1182abfabf8cSAndy Adamson 1183abfabf8cSAndy Adamson /* Encode the replayed sequence operation */ 1184abfabf8cSAndy Adamson op = &args->ops[resp->opcnt - 1]; 1185abfabf8cSAndy Adamson nfsd4_encode_operation(resp, op); 1186abfabf8cSAndy Adamson 1187abfabf8cSAndy Adamson /* Return nfserr_retry_uncached_rep in next operation. */ 1188557ce264SAndy Adamson if (args->opcnt > 1 && slot->sl_cachethis == 0) { 1189abfabf8cSAndy Adamson op = &args->ops[resp->opcnt++]; 1190abfabf8cSAndy Adamson op->status = nfserr_retry_uncached_rep; 1191abfabf8cSAndy Adamson nfsd4_encode_operation(resp, op); 1192074fe897SAndy Adamson } 1193abfabf8cSAndy Adamson return op->status; 1194074fe897SAndy Adamson } 1195074fe897SAndy Adamson 1196074fe897SAndy Adamson /* 1197557ce264SAndy Adamson * The sequence operation is not cached because we can use the slot and 1198557ce264SAndy Adamson * session values. 1199074fe897SAndy Adamson */ 1200074fe897SAndy Adamson __be32 1201bf864a31SAndy Adamson nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, 1202bf864a31SAndy Adamson struct nfsd4_sequence *seq) 1203074fe897SAndy Adamson { 1204557ce264SAndy Adamson struct nfsd4_slot *slot = resp->cstate.slot; 1205074fe897SAndy Adamson __be32 status; 1206074fe897SAndy Adamson 1207557ce264SAndy Adamson dprintk("--> %s slot %p\n", __func__, slot); 1208074fe897SAndy Adamson 1209abfabf8cSAndy Adamson /* Either returns 0 or nfserr_retry_uncached */ 1210abfabf8cSAndy Adamson status = nfsd4_enc_sequence_replay(resp->rqstp->rq_argp, resp); 1211abfabf8cSAndy Adamson if (status == nfserr_retry_uncached_rep) 1212abfabf8cSAndy Adamson return status; 1213074fe897SAndy Adamson 1214557ce264SAndy Adamson /* The sequence operation has been encoded, cstate->datap set. */ 1215557ce264SAndy Adamson memcpy(resp->cstate.datap, slot->sl_data, slot->sl_datalen); 1216074fe897SAndy Adamson 1217557ce264SAndy Adamson resp->opcnt = slot->sl_opcnt; 1218557ce264SAndy Adamson resp->p = resp->cstate.datap + XDR_QUADLEN(slot->sl_datalen); 1219557ce264SAndy Adamson status = slot->sl_status; 1220074fe897SAndy Adamson 1221074fe897SAndy Adamson return status; 1222074fe897SAndy Adamson } 1223074fe897SAndy Adamson 12240733d213SAndy Adamson /* 12250733d213SAndy Adamson * Set the exchange_id flags returned by the server. 12260733d213SAndy Adamson */ 12270733d213SAndy Adamson static void 12280733d213SAndy Adamson nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid) 12290733d213SAndy Adamson { 12300733d213SAndy Adamson /* pNFS is not supported */ 12310733d213SAndy Adamson new->cl_exchange_flags |= EXCHGID4_FLAG_USE_NON_PNFS; 12320733d213SAndy Adamson 12330733d213SAndy Adamson /* Referrals are supported, Migration is not. */ 12340733d213SAndy Adamson new->cl_exchange_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER; 12350733d213SAndy Adamson 12360733d213SAndy Adamson /* set the wire flags to return to client. */ 12370733d213SAndy Adamson clid->flags = new->cl_exchange_flags; 12380733d213SAndy Adamson } 12390733d213SAndy Adamson 1240b37ad28bSAl Viro __be32 1241069b6ad4SAndy Adamson nfsd4_exchange_id(struct svc_rqst *rqstp, 1242069b6ad4SAndy Adamson struct nfsd4_compound_state *cstate, 1243069b6ad4SAndy Adamson struct nfsd4_exchange_id *exid) 1244069b6ad4SAndy Adamson { 12450733d213SAndy Adamson struct nfs4_client *unconf, *conf, *new; 12460733d213SAndy Adamson int status; 12470733d213SAndy Adamson unsigned int strhashval; 12480733d213SAndy Adamson char dname[HEXDIR_LEN]; 1249363168b4SJeff Layton char addr_str[INET6_ADDRSTRLEN]; 12500733d213SAndy Adamson nfs4_verifier verf = exid->verifier; 1251363168b4SJeff Layton struct sockaddr *sa = svc_addr(rqstp); 12520733d213SAndy Adamson 1253363168b4SJeff Layton rpc_ntop(sa, addr_str, sizeof(addr_str)); 12540733d213SAndy Adamson dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p " 1255363168b4SJeff Layton "ip_addr=%s flags %x, spa_how %d\n", 12560733d213SAndy Adamson __func__, rqstp, exid, exid->clname.len, exid->clname.data, 1257363168b4SJeff Layton addr_str, exid->flags, exid->spa_how); 12580733d213SAndy Adamson 12590733d213SAndy Adamson if (!check_name(exid->clname) || (exid->flags & ~EXCHGID4_FLAG_MASK_A)) 12600733d213SAndy Adamson return nfserr_inval; 12610733d213SAndy Adamson 12620733d213SAndy Adamson /* Currently only support SP4_NONE */ 12630733d213SAndy Adamson switch (exid->spa_how) { 12640733d213SAndy Adamson case SP4_NONE: 12650733d213SAndy Adamson break; 12660733d213SAndy Adamson case SP4_SSV: 12670733d213SAndy Adamson return nfserr_encr_alg_unsupp; 12680733d213SAndy Adamson default: 12690733d213SAndy Adamson BUG(); /* checked by xdr code */ 12700733d213SAndy Adamson case SP4_MACH_CRED: 12710733d213SAndy Adamson return nfserr_serverfault; /* no excuse :-/ */ 12720733d213SAndy Adamson } 12730733d213SAndy Adamson 12740733d213SAndy Adamson status = nfs4_make_rec_clidname(dname, &exid->clname); 12750733d213SAndy Adamson 12760733d213SAndy Adamson if (status) 12770733d213SAndy Adamson goto error; 12780733d213SAndy Adamson 12790733d213SAndy Adamson strhashval = clientstr_hashval(dname); 12800733d213SAndy Adamson 12810733d213SAndy Adamson nfs4_lock_state(); 12820733d213SAndy Adamson status = nfs_ok; 12830733d213SAndy Adamson 1284a1bcecd2SAndy Adamson conf = find_confirmed_client_by_str(dname, strhashval, true); 12850733d213SAndy Adamson if (conf) { 12860733d213SAndy Adamson if (!same_verf(&verf, &conf->cl_verifier)) { 12870733d213SAndy Adamson /* 18.35.4 case 8 */ 12880733d213SAndy Adamson if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { 12890733d213SAndy Adamson status = nfserr_not_same; 12900733d213SAndy Adamson goto out; 12910733d213SAndy Adamson } 12920733d213SAndy Adamson /* Client reboot: destroy old state */ 12930733d213SAndy Adamson expire_client(conf); 12940733d213SAndy Adamson goto out_new; 12950733d213SAndy Adamson } 12960733d213SAndy Adamson if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { 12970733d213SAndy Adamson /* 18.35.4 case 9 */ 12980733d213SAndy Adamson if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { 12990733d213SAndy Adamson status = nfserr_perm; 13000733d213SAndy Adamson goto out; 13010733d213SAndy Adamson } 13020733d213SAndy Adamson expire_client(conf); 13030733d213SAndy Adamson goto out_new; 13040733d213SAndy Adamson } 13050733d213SAndy Adamson /* 13060733d213SAndy Adamson * Set bit when the owner id and verifier map to an already 13070733d213SAndy Adamson * confirmed client id (18.35.3). 13080733d213SAndy Adamson */ 13090733d213SAndy Adamson exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; 13100733d213SAndy Adamson 13110733d213SAndy Adamson /* 13120733d213SAndy Adamson * Falling into 18.35.4 case 2, possible router replay. 13130733d213SAndy Adamson * Leave confirmed record intact and return same result. 13140733d213SAndy Adamson */ 13150733d213SAndy Adamson copy_verf(conf, &verf); 13160733d213SAndy Adamson new = conf; 13170733d213SAndy Adamson goto out_copy; 13186ddbbbfeSMike Sager } 13196ddbbbfeSMike Sager 13200733d213SAndy Adamson /* 18.35.4 case 7 */ 13210733d213SAndy Adamson if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { 13220733d213SAndy Adamson status = nfserr_noent; 13230733d213SAndy Adamson goto out; 13240733d213SAndy Adamson } 13250733d213SAndy Adamson 1326a1bcecd2SAndy Adamson unconf = find_unconfirmed_client_by_str(dname, strhashval, true); 13270733d213SAndy Adamson if (unconf) { 13280733d213SAndy Adamson /* 13290733d213SAndy Adamson * Possible retry or client restart. Per 18.35.4 case 4, 13300733d213SAndy Adamson * a new unconfirmed record should be generated regardless 13310733d213SAndy Adamson * of whether any properties have changed. 13320733d213SAndy Adamson */ 13330733d213SAndy Adamson expire_client(unconf); 13340733d213SAndy Adamson } 13350733d213SAndy Adamson 13360733d213SAndy Adamson out_new: 13370733d213SAndy Adamson /* Normal case */ 1338b09333c4SRicardo Labiaga new = create_client(exid->clname, dname, rqstp, &verf); 13390733d213SAndy Adamson if (new == NULL) { 13404731030dSJ. Bruce Fields status = nfserr_jukebox; 13410733d213SAndy Adamson goto out; 13420733d213SAndy Adamson } 13430733d213SAndy Adamson 13440733d213SAndy Adamson gen_clid(new); 13450733d213SAndy Adamson add_to_unconfirmed(new, strhashval); 13460733d213SAndy Adamson out_copy: 13470733d213SAndy Adamson exid->clientid.cl_boot = new->cl_clientid.cl_boot; 13480733d213SAndy Adamson exid->clientid.cl_id = new->cl_clientid.cl_id; 13490733d213SAndy Adamson 135038eb76a5SAndy Adamson exid->seqid = 1; 13510733d213SAndy Adamson nfsd4_set_ex_flags(new, exid); 13520733d213SAndy Adamson 13530733d213SAndy Adamson dprintk("nfsd4_exchange_id seqid %d flags %x\n", 135449557cc7SAndy Adamson new->cl_cs_slot.sl_seqid, new->cl_exchange_flags); 13550733d213SAndy Adamson status = nfs_ok; 13560733d213SAndy Adamson 13570733d213SAndy Adamson out: 13580733d213SAndy Adamson nfs4_unlock_state(); 13590733d213SAndy Adamson error: 13600733d213SAndy Adamson dprintk("nfsd4_exchange_id returns %d\n", ntohl(status)); 13610733d213SAndy Adamson return status; 1362069b6ad4SAndy Adamson } 1363069b6ad4SAndy Adamson 1364b85d4c01SBenny Halevy static int 136588e588d5SAndy Adamson check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse) 1366b85d4c01SBenny Halevy { 136788e588d5SAndy Adamson dprintk("%s enter. seqid %d slot_seqid %d\n", __func__, seqid, 136888e588d5SAndy Adamson slot_seqid); 1369b85d4c01SBenny Halevy 1370b85d4c01SBenny Halevy /* The slot is in use, and no response has been sent. */ 137188e588d5SAndy Adamson if (slot_inuse) { 137288e588d5SAndy Adamson if (seqid == slot_seqid) 1373b85d4c01SBenny Halevy return nfserr_jukebox; 1374b85d4c01SBenny Halevy else 1375b85d4c01SBenny Halevy return nfserr_seq_misordered; 1376b85d4c01SBenny Halevy } 1377b85d4c01SBenny Halevy /* Normal */ 137888e588d5SAndy Adamson if (likely(seqid == slot_seqid + 1)) 1379b85d4c01SBenny Halevy return nfs_ok; 1380b85d4c01SBenny Halevy /* Replay */ 138188e588d5SAndy Adamson if (seqid == slot_seqid) 1382b85d4c01SBenny Halevy return nfserr_replay_cache; 1383b85d4c01SBenny Halevy /* Wraparound */ 138488e588d5SAndy Adamson if (seqid == 1 && (slot_seqid + 1) == 0) 1385b85d4c01SBenny Halevy return nfs_ok; 1386b85d4c01SBenny Halevy /* Misordered replay or misordered new request */ 1387b85d4c01SBenny Halevy return nfserr_seq_misordered; 1388b85d4c01SBenny Halevy } 1389b85d4c01SBenny Halevy 139049557cc7SAndy Adamson /* 139149557cc7SAndy Adamson * Cache the create session result into the create session single DRC 139249557cc7SAndy Adamson * slot cache by saving the xdr structure. sl_seqid has been set. 139349557cc7SAndy Adamson * Do this for solo or embedded create session operations. 139449557cc7SAndy Adamson */ 139549557cc7SAndy Adamson static void 139649557cc7SAndy Adamson nfsd4_cache_create_session(struct nfsd4_create_session *cr_ses, 139749557cc7SAndy Adamson struct nfsd4_clid_slot *slot, int nfserr) 139849557cc7SAndy Adamson { 139949557cc7SAndy Adamson slot->sl_status = nfserr; 140049557cc7SAndy Adamson memcpy(&slot->sl_cr_ses, cr_ses, sizeof(*cr_ses)); 140149557cc7SAndy Adamson } 140249557cc7SAndy Adamson 140349557cc7SAndy Adamson static __be32 140449557cc7SAndy Adamson nfsd4_replay_create_session(struct nfsd4_create_session *cr_ses, 140549557cc7SAndy Adamson struct nfsd4_clid_slot *slot) 140649557cc7SAndy Adamson { 140749557cc7SAndy Adamson memcpy(cr_ses, &slot->sl_cr_ses, sizeof(*cr_ses)); 140849557cc7SAndy Adamson return slot->sl_status; 140949557cc7SAndy Adamson } 141049557cc7SAndy Adamson 1411069b6ad4SAndy Adamson __be32 1412069b6ad4SAndy Adamson nfsd4_create_session(struct svc_rqst *rqstp, 1413069b6ad4SAndy Adamson struct nfsd4_compound_state *cstate, 1414069b6ad4SAndy Adamson struct nfsd4_create_session *cr_ses) 1415069b6ad4SAndy Adamson { 1416363168b4SJeff Layton struct sockaddr *sa = svc_addr(rqstp); 1417ec6b5d7bSAndy Adamson struct nfs4_client *conf, *unconf; 141849557cc7SAndy Adamson struct nfsd4_clid_slot *cs_slot = NULL; 1419ec6b5d7bSAndy Adamson int status = 0; 1420ec6b5d7bSAndy Adamson 1421ec6b5d7bSAndy Adamson nfs4_lock_state(); 1422ec6b5d7bSAndy Adamson unconf = find_unconfirmed_client(&cr_ses->clientid); 1423ec6b5d7bSAndy Adamson conf = find_confirmed_client(&cr_ses->clientid); 1424ec6b5d7bSAndy Adamson 1425ec6b5d7bSAndy Adamson if (conf) { 142649557cc7SAndy Adamson cs_slot = &conf->cl_cs_slot; 142749557cc7SAndy Adamson status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); 142838eb76a5SAndy Adamson if (status == nfserr_replay_cache) { 1429ec6b5d7bSAndy Adamson dprintk("Got a create_session replay! seqid= %d\n", 143049557cc7SAndy Adamson cs_slot->sl_seqid); 143138eb76a5SAndy Adamson /* Return the cached reply status */ 143249557cc7SAndy Adamson status = nfsd4_replay_create_session(cr_ses, cs_slot); 143338eb76a5SAndy Adamson goto out; 143449557cc7SAndy Adamson } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) { 1435ec6b5d7bSAndy Adamson status = nfserr_seq_misordered; 1436ec6b5d7bSAndy Adamson dprintk("Sequence misordered!\n"); 1437ec6b5d7bSAndy Adamson dprintk("Expected seqid= %d but got seqid= %d\n", 143849557cc7SAndy Adamson cs_slot->sl_seqid, cr_ses->seqid); 1439ec6b5d7bSAndy Adamson goto out; 1440ec6b5d7bSAndy Adamson } 144149557cc7SAndy Adamson cs_slot->sl_seqid++; 1442ec6b5d7bSAndy Adamson } else if (unconf) { 1443ec6b5d7bSAndy Adamson if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || 1444363168b4SJeff Layton !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { 1445ec6b5d7bSAndy Adamson status = nfserr_clid_inuse; 1446ec6b5d7bSAndy Adamson goto out; 1447ec6b5d7bSAndy Adamson } 1448ec6b5d7bSAndy Adamson 144949557cc7SAndy Adamson cs_slot = &unconf->cl_cs_slot; 145049557cc7SAndy Adamson status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); 145138eb76a5SAndy Adamson if (status) { 145238eb76a5SAndy Adamson /* an unconfirmed replay returns misordered */ 1453ec6b5d7bSAndy Adamson status = nfserr_seq_misordered; 145449557cc7SAndy Adamson goto out_cache; 1455ec6b5d7bSAndy Adamson } 1456ec6b5d7bSAndy Adamson 145749557cc7SAndy Adamson cs_slot->sl_seqid++; /* from 0 to 1 */ 1458ec6b5d7bSAndy Adamson move_to_confirmed(unconf); 1459ec6b5d7bSAndy Adamson 146038524ab3SAndy Adamson if (cr_ses->flags & SESSION4_BACK_CHAN) { 14612bf23875SJ. Bruce Fields unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt; 14622bf23875SJ. Bruce Fields svc_xprt_get(rqstp->rq_xprt); 146338524ab3SAndy Adamson rpc_copy_addr( 146438524ab3SAndy Adamson (struct sockaddr *)&unconf->cl_cb_conn.cb_addr, 146538524ab3SAndy Adamson sa); 146638524ab3SAndy Adamson unconf->cl_cb_conn.cb_addrlen = svc_addr_len(sa); 146738524ab3SAndy Adamson unconf->cl_cb_conn.cb_minorversion = 146838524ab3SAndy Adamson cstate->minorversion; 146938524ab3SAndy Adamson unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog; 14702a1d1b59SRicardo Labiaga unconf->cl_cb_seq_nr = 1; 14714b21d0deSJ. Bruce Fields nfsd4_probe_callback(unconf, &unconf->cl_cb_conn); 147238524ab3SAndy Adamson } 1473ec6b5d7bSAndy Adamson conf = unconf; 1474ec6b5d7bSAndy Adamson } else { 1475ec6b5d7bSAndy Adamson status = nfserr_stale_clientid; 1476ec6b5d7bSAndy Adamson goto out; 1477ec6b5d7bSAndy Adamson } 1478ec6b5d7bSAndy Adamson 1479408b79bcSJ. Bruce Fields /* 1480408b79bcSJ. Bruce Fields * We do not support RDMA or persistent sessions 1481408b79bcSJ. Bruce Fields */ 1482408b79bcSJ. Bruce Fields cr_ses->flags &= ~SESSION4_PERSIST; 1483408b79bcSJ. Bruce Fields cr_ses->flags &= ~SESSION4_RDMA; 1484408b79bcSJ. Bruce Fields 1485ec6b5d7bSAndy Adamson status = alloc_init_session(rqstp, conf, cr_ses); 1486ec6b5d7bSAndy Adamson if (status) 1487ec6b5d7bSAndy Adamson goto out; 1488ec6b5d7bSAndy Adamson 1489ec6b5d7bSAndy Adamson memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data, 1490ec6b5d7bSAndy Adamson NFS4_MAX_SESSIONID_LEN); 149149557cc7SAndy Adamson cr_ses->seqid = cs_slot->sl_seqid; 1492ec6b5d7bSAndy Adamson 149349557cc7SAndy Adamson out_cache: 149449557cc7SAndy Adamson /* cache solo and embedded create sessions under the state lock */ 149549557cc7SAndy Adamson nfsd4_cache_create_session(cr_ses, cs_slot, status); 1496ec6b5d7bSAndy Adamson out: 1497ec6b5d7bSAndy Adamson nfs4_unlock_state(); 1498ec6b5d7bSAndy Adamson dprintk("%s returns %d\n", __func__, ntohl(status)); 1499ec6b5d7bSAndy Adamson return status; 1500069b6ad4SAndy Adamson } 1501069b6ad4SAndy Adamson 150257716355SJ. Bruce Fields static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) 150357716355SJ. Bruce Fields { 150457716355SJ. Bruce Fields struct nfsd4_compoundres *resp = rqstp->rq_resp; 150557716355SJ. Bruce Fields struct nfsd4_compoundargs *argp = rqstp->rq_argp; 150657716355SJ. Bruce Fields 150757716355SJ. Bruce Fields return argp->opcnt == resp->opcnt; 150857716355SJ. Bruce Fields } 150957716355SJ. Bruce Fields 15105d4cec2fSJ. Bruce Fields static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) 15115d4cec2fSJ. Bruce Fields { 15125d4cec2fSJ. Bruce Fields if (!session) 15135d4cec2fSJ. Bruce Fields return 0; 15145d4cec2fSJ. Bruce Fields return !memcmp(sid, &session->se_sessionid, sizeof(*sid)); 15155d4cec2fSJ. Bruce Fields } 15165d4cec2fSJ. Bruce Fields 1517069b6ad4SAndy Adamson __be32 1518069b6ad4SAndy Adamson nfsd4_destroy_session(struct svc_rqst *r, 1519069b6ad4SAndy Adamson struct nfsd4_compound_state *cstate, 1520069b6ad4SAndy Adamson struct nfsd4_destroy_session *sessionid) 1521069b6ad4SAndy Adamson { 1522e10e0cfcSBenny Halevy struct nfsd4_session *ses; 1523e10e0cfcSBenny Halevy u32 status = nfserr_badsession; 1524e10e0cfcSBenny Halevy 1525e10e0cfcSBenny Halevy /* Notes: 1526e10e0cfcSBenny Halevy * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid 1527e10e0cfcSBenny Halevy * - Should we return nfserr_back_chan_busy if waiting for 1528e10e0cfcSBenny Halevy * callbacks on to-be-destroyed session? 1529e10e0cfcSBenny Halevy * - Do we need to clear any callback info from previous session? 1530e10e0cfcSBenny Halevy */ 1531e10e0cfcSBenny Halevy 15325d4cec2fSJ. Bruce Fields if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { 153357716355SJ. Bruce Fields if (!nfsd4_last_compound_op(r)) 153457716355SJ. Bruce Fields return nfserr_not_only_op; 153557716355SJ. Bruce Fields } 1536e10e0cfcSBenny Halevy dump_sessionid(__func__, &sessionid->sessionid); 15379089f1b4SBenny Halevy spin_lock(&client_lock); 1538e10e0cfcSBenny Halevy ses = find_in_sessionid_hashtbl(&sessionid->sessionid); 1539e10e0cfcSBenny Halevy if (!ses) { 15409089f1b4SBenny Halevy spin_unlock(&client_lock); 1541e10e0cfcSBenny Halevy goto out; 1542e10e0cfcSBenny Halevy } 1543e10e0cfcSBenny Halevy 1544e10e0cfcSBenny Halevy unhash_session(ses); 15459089f1b4SBenny Halevy spin_unlock(&client_lock); 1546e10e0cfcSBenny Halevy 1547ab707e15SBenny Halevy nfs4_lock_state(); 1548e10e0cfcSBenny Halevy /* wait for callbacks */ 1549*6ff8da08SJ. Bruce Fields nfsd4_shutdown_callback(ses->se_client); 1550ab707e15SBenny Halevy nfs4_unlock_state(); 1551e10e0cfcSBenny Halevy nfsd4_put_session(ses); 1552e10e0cfcSBenny Halevy status = nfs_ok; 1553e10e0cfcSBenny Halevy out: 1554e10e0cfcSBenny Halevy dprintk("%s returns %d\n", __func__, ntohl(status)); 1555e10e0cfcSBenny Halevy return status; 1556069b6ad4SAndy Adamson } 1557069b6ad4SAndy Adamson 1558069b6ad4SAndy Adamson __be32 1559b85d4c01SBenny Halevy nfsd4_sequence(struct svc_rqst *rqstp, 1560069b6ad4SAndy Adamson struct nfsd4_compound_state *cstate, 1561069b6ad4SAndy Adamson struct nfsd4_sequence *seq) 1562069b6ad4SAndy Adamson { 1563f9bb94c4SAndy Adamson struct nfsd4_compoundres *resp = rqstp->rq_resp; 1564b85d4c01SBenny Halevy struct nfsd4_session *session; 1565b85d4c01SBenny Halevy struct nfsd4_slot *slot; 1566b85d4c01SBenny Halevy int status; 1567b85d4c01SBenny Halevy 1568f9bb94c4SAndy Adamson if (resp->opcnt != 1) 1569f9bb94c4SAndy Adamson return nfserr_sequence_pos; 1570f9bb94c4SAndy Adamson 15719089f1b4SBenny Halevy spin_lock(&client_lock); 1572b85d4c01SBenny Halevy status = nfserr_badsession; 1573b85d4c01SBenny Halevy session = find_in_sessionid_hashtbl(&seq->sessionid); 1574b85d4c01SBenny Halevy if (!session) 1575b85d4c01SBenny Halevy goto out; 1576b85d4c01SBenny Halevy 1577b85d4c01SBenny Halevy status = nfserr_badslot; 15786c18ba9fSAlexandros Batsakis if (seq->slotid >= session->se_fchannel.maxreqs) 1579b85d4c01SBenny Halevy goto out; 1580b85d4c01SBenny Halevy 1581557ce264SAndy Adamson slot = session->se_slots[seq->slotid]; 1582b85d4c01SBenny Halevy dprintk("%s: slotid %d\n", __func__, seq->slotid); 1583b85d4c01SBenny Halevy 1584a8dfdaebSAndy Adamson /* We do not negotiate the number of slots yet, so set the 1585a8dfdaebSAndy Adamson * maxslots to the session maxreqs which is used to encode 1586a8dfdaebSAndy Adamson * sr_highest_slotid and the sr_target_slot id to maxslots */ 1587a8dfdaebSAndy Adamson seq->maxslots = session->se_fchannel.maxreqs; 1588a8dfdaebSAndy Adamson 158988e588d5SAndy Adamson status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_inuse); 1590b85d4c01SBenny Halevy if (status == nfserr_replay_cache) { 1591b85d4c01SBenny Halevy cstate->slot = slot; 1592b85d4c01SBenny Halevy cstate->session = session; 1593da3846a2SAndy Adamson /* Return the cached reply status and set cstate->status 1594557ce264SAndy Adamson * for nfsd4_proc_compound processing */ 1595bf864a31SAndy Adamson status = nfsd4_replay_cache_entry(resp, seq); 1596da3846a2SAndy Adamson cstate->status = nfserr_replay_cache; 1597aaf84eb9SBenny Halevy goto out; 1598b85d4c01SBenny Halevy } 1599b85d4c01SBenny Halevy if (status) 1600b85d4c01SBenny Halevy goto out; 1601b85d4c01SBenny Halevy 1602b85d4c01SBenny Halevy /* Success! bump slot seqid */ 1603b85d4c01SBenny Halevy slot->sl_inuse = true; 1604b85d4c01SBenny Halevy slot->sl_seqid = seq->seqid; 1605557ce264SAndy Adamson slot->sl_cachethis = seq->cachethis; 1606b85d4c01SBenny Halevy 1607b85d4c01SBenny Halevy cstate->slot = slot; 1608b85d4c01SBenny Halevy cstate->session = session; 1609b85d4c01SBenny Halevy 1610b85d4c01SBenny Halevy out: 161126c0c75eSJ. Bruce Fields /* Hold a session reference until done processing the compound. */ 1612aaf84eb9SBenny Halevy if (cstate->session) { 161336acb66bSBenny Halevy nfsd4_get_session(cstate->session); 1614d7682988SBenny Halevy atomic_inc(&session->se_client->cl_refcount); 1615aaf84eb9SBenny Halevy } 161636acb66bSBenny Halevy spin_unlock(&client_lock); 1617b85d4c01SBenny Halevy dprintk("%s: return %d\n", __func__, ntohl(status)); 1618b85d4c01SBenny Halevy return status; 1619069b6ad4SAndy Adamson } 1620069b6ad4SAndy Adamson 1621069b6ad4SAndy Adamson __be32 16224dc6ec00SJ. Bruce Fields nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_reclaim_complete *rc) 16234dc6ec00SJ. Bruce Fields { 16244dc6ec00SJ. Bruce Fields if (rc->rca_one_fs) { 16254dc6ec00SJ. Bruce Fields if (!cstate->current_fh.fh_dentry) 16264dc6ec00SJ. Bruce Fields return nfserr_nofilehandle; 16274dc6ec00SJ. Bruce Fields /* 16284dc6ec00SJ. Bruce Fields * We don't take advantage of the rca_one_fs case. 16294dc6ec00SJ. Bruce Fields * That's OK, it's optional, we can safely ignore it. 16304dc6ec00SJ. Bruce Fields */ 16314dc6ec00SJ. Bruce Fields return nfs_ok; 16324dc6ec00SJ. Bruce Fields } 16334dc6ec00SJ. Bruce Fields nfs4_lock_state(); 16344dc6ec00SJ. Bruce Fields if (is_client_expired(cstate->session->se_client)) { 16354dc6ec00SJ. Bruce Fields nfs4_unlock_state(); 16364dc6ec00SJ. Bruce Fields /* 16374dc6ec00SJ. Bruce Fields * The following error isn't really legal. 16384dc6ec00SJ. Bruce Fields * But we only get here if the client just explicitly 16394dc6ec00SJ. Bruce Fields * destroyed the client. Surely it no longer cares what 16404dc6ec00SJ. Bruce Fields * error it gets back on an operation for the dead 16414dc6ec00SJ. Bruce Fields * client. 16424dc6ec00SJ. Bruce Fields */ 16434dc6ec00SJ. Bruce Fields return nfserr_stale_clientid; 16444dc6ec00SJ. Bruce Fields } 16454dc6ec00SJ. Bruce Fields nfsd4_create_clid_dir(cstate->session->se_client); 16464dc6ec00SJ. Bruce Fields nfs4_unlock_state(); 16474dc6ec00SJ. Bruce Fields return nfs_ok; 16484dc6ec00SJ. Bruce Fields } 16494dc6ec00SJ. Bruce Fields 16504dc6ec00SJ. Bruce Fields __be32 1651b591480bSJ.Bruce Fields nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 1652b591480bSJ.Bruce Fields struct nfsd4_setclientid *setclid) 16531da177e4SLinus Torvalds { 1654363168b4SJeff Layton struct sockaddr *sa = svc_addr(rqstp); 16551da177e4SLinus Torvalds struct xdr_netobj clname = { 16561da177e4SLinus Torvalds .len = setclid->se_namelen, 16571da177e4SLinus Torvalds .data = setclid->se_name, 16581da177e4SLinus Torvalds }; 16591da177e4SLinus Torvalds nfs4_verifier clverifier = setclid->se_verf; 16601da177e4SLinus Torvalds unsigned int strhashval; 166128ce6054SNeilBrown struct nfs4_client *conf, *unconf, *new; 1662b37ad28bSAl Viro __be32 status; 1663a55370a3SNeilBrown char dname[HEXDIR_LEN]; 16641da177e4SLinus Torvalds 16651da177e4SLinus Torvalds if (!check_name(clname)) 166673aea4ecSNeil Brown return nfserr_inval; 16671da177e4SLinus Torvalds 1668a55370a3SNeilBrown status = nfs4_make_rec_clidname(dname, &clname); 1669a55370a3SNeilBrown if (status) 167073aea4ecSNeil Brown return status; 1671a55370a3SNeilBrown 16721da177e4SLinus Torvalds /* 16731da177e4SLinus Torvalds * XXX The Duplicate Request Cache (DRC) has been checked (??) 16741da177e4SLinus Torvalds * We get here on a DRC miss. 16751da177e4SLinus Torvalds */ 16761da177e4SLinus Torvalds 1677a55370a3SNeilBrown strhashval = clientstr_hashval(dname); 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds nfs4_lock_state(); 1680a1bcecd2SAndy Adamson conf = find_confirmed_client_by_str(dname, strhashval, false); 168128ce6054SNeilBrown if (conf) { 1682a186e767SJ. Bruce Fields /* RFC 3530 14.2.33 CASE 0: */ 16831da177e4SLinus Torvalds status = nfserr_clid_inuse; 1684026722c2SJ. Bruce Fields if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { 1685363168b4SJeff Layton char addr_str[INET6_ADDRSTRLEN]; 1686363168b4SJeff Layton rpc_ntop((struct sockaddr *) &conf->cl_addr, addr_str, 1687363168b4SJeff Layton sizeof(addr_str)); 1688026722c2SJ. Bruce Fields dprintk("NFSD: setclientid: string in use by client " 1689363168b4SJeff Layton "at %s\n", addr_str); 16901da177e4SLinus Torvalds goto out; 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds } 1693a186e767SJ. Bruce Fields /* 1694a186e767SJ. Bruce Fields * section 14.2.33 of RFC 3530 (under the heading "IMPLEMENTATION") 1695a186e767SJ. Bruce Fields * has a description of SETCLIENTID request processing consisting 1696a186e767SJ. Bruce Fields * of 5 bullet points, labeled as CASE0 - CASE4 below. 1697a186e767SJ. Bruce Fields */ 1698a1bcecd2SAndy Adamson unconf = find_unconfirmed_client_by_str(dname, strhashval, false); 16991da177e4SLinus Torvalds status = nfserr_resource; 17001da177e4SLinus Torvalds if (!conf) { 17011da177e4SLinus Torvalds /* 1702a186e767SJ. Bruce Fields * RFC 3530 14.2.33 CASE 4: 1703a186e767SJ. Bruce Fields * placed first, because it is the normal case 17041da177e4SLinus Torvalds */ 17051da177e4SLinus Torvalds if (unconf) 17061da177e4SLinus Torvalds expire_client(unconf); 1707b09333c4SRicardo Labiaga new = create_client(clname, dname, rqstp, &clverifier); 1708a55370a3SNeilBrown if (new == NULL) 17091da177e4SLinus Torvalds goto out; 17101da177e4SLinus Torvalds gen_clid(new); 1711599e0a22SJ. Bruce Fields } else if (same_verf(&conf->cl_verifier, &clverifier)) { 17121da177e4SLinus Torvalds /* 1713a186e767SJ. Bruce Fields * RFC 3530 14.2.33 CASE 1: 1714a186e767SJ. Bruce Fields * probable callback update 17151da177e4SLinus Torvalds */ 171631f4a6c1SNeilBrown if (unconf) { 171731f4a6c1SNeilBrown /* Note this is removing unconfirmed {*x***}, 171831f4a6c1SNeilBrown * which is stronger than RFC recommended {vxc**}. 171931f4a6c1SNeilBrown * This has the advantage that there is at most 172031f4a6c1SNeilBrown * one {*x***} in either list at any time. 172131f4a6c1SNeilBrown */ 17221da177e4SLinus Torvalds expire_client(unconf); 17231da177e4SLinus Torvalds } 1724b09333c4SRicardo Labiaga new = create_client(clname, dname, rqstp, &clverifier); 1725a55370a3SNeilBrown if (new == NULL) 17261da177e4SLinus Torvalds goto out; 17271da177e4SLinus Torvalds copy_clid(new, conf); 17281da177e4SLinus Torvalds } else if (!unconf) { 17291da177e4SLinus Torvalds /* 1730a186e767SJ. Bruce Fields * RFC 3530 14.2.33 CASE 2: 1731a186e767SJ. Bruce Fields * probable client reboot; state will be removed if 1732a186e767SJ. Bruce Fields * confirmed. 17331da177e4SLinus Torvalds */ 1734b09333c4SRicardo Labiaga new = create_client(clname, dname, rqstp, &clverifier); 1735a55370a3SNeilBrown if (new == NULL) 17361da177e4SLinus Torvalds goto out; 17371da177e4SLinus Torvalds gen_clid(new); 173849ba8781SJ. Bruce Fields } else { 17391da177e4SLinus Torvalds /* 1740a186e767SJ. Bruce Fields * RFC 3530 14.2.33 CASE 3: 1741a186e767SJ. Bruce Fields * probable client reboot; state will be removed if 1742a186e767SJ. Bruce Fields * confirmed. 17431da177e4SLinus Torvalds */ 17441da177e4SLinus Torvalds expire_client(unconf); 1745b09333c4SRicardo Labiaga new = create_client(clname, dname, rqstp, &clverifier); 1746a55370a3SNeilBrown if (new == NULL) 17471da177e4SLinus Torvalds goto out; 17481da177e4SLinus Torvalds gen_clid(new); 17491da177e4SLinus Torvalds } 1750fbf4665fSJeff Layton gen_callback(new, setclid, rpc_get_scope_id(sa)); 1751c175b83cSJ. Bruce Fields add_to_unconfirmed(new, strhashval); 17521da177e4SLinus Torvalds setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot; 17531da177e4SLinus Torvalds setclid->se_clientid.cl_id = new->cl_clientid.cl_id; 17541da177e4SLinus Torvalds memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data)); 17551da177e4SLinus Torvalds status = nfs_ok; 17561da177e4SLinus Torvalds out: 17571da177e4SLinus Torvalds nfs4_unlock_state(); 17581da177e4SLinus Torvalds return status; 17591da177e4SLinus Torvalds } 17601da177e4SLinus Torvalds 17611da177e4SLinus Torvalds 17621da177e4SLinus Torvalds /* 1763a186e767SJ. Bruce Fields * Section 14.2.34 of RFC 3530 (under the heading "IMPLEMENTATION") has 1764a186e767SJ. Bruce Fields * a description of SETCLIENTID_CONFIRM request processing consisting of 4 1765a186e767SJ. Bruce Fields * bullets, labeled as CASE1 - CASE4 below. 17661da177e4SLinus Torvalds */ 1767b37ad28bSAl Viro __be32 1768b591480bSJ.Bruce Fields nfsd4_setclientid_confirm(struct svc_rqst *rqstp, 1769b591480bSJ.Bruce Fields struct nfsd4_compound_state *cstate, 1770b591480bSJ.Bruce Fields struct nfsd4_setclientid_confirm *setclientid_confirm) 17711da177e4SLinus Torvalds { 1772363168b4SJeff Layton struct sockaddr *sa = svc_addr(rqstp); 177321ab45a4SNeilBrown struct nfs4_client *conf, *unconf; 17741da177e4SLinus Torvalds nfs4_verifier confirm = setclientid_confirm->sc_confirm; 17751da177e4SLinus Torvalds clientid_t * clid = &setclientid_confirm->sc_clientid; 1776b37ad28bSAl Viro __be32 status; 17771da177e4SLinus Torvalds 17781da177e4SLinus Torvalds if (STALE_CLIENTID(clid)) 17791da177e4SLinus Torvalds return nfserr_stale_clientid; 17801da177e4SLinus Torvalds /* 17811da177e4SLinus Torvalds * XXX The Duplicate Request Cache (DRC) has been checked (??) 17821da177e4SLinus Torvalds * We get here on a DRC miss. 17831da177e4SLinus Torvalds */ 17841da177e4SLinus Torvalds 17851da177e4SLinus Torvalds nfs4_lock_state(); 178621ab45a4SNeilBrown 178721ab45a4SNeilBrown conf = find_confirmed_client(clid); 178821ab45a4SNeilBrown unconf = find_unconfirmed_client(clid); 178921ab45a4SNeilBrown 179022de4d83SNeilBrown status = nfserr_clid_inuse; 1791363168b4SJeff Layton if (conf && !rpc_cmp_addr((struct sockaddr *) &conf->cl_addr, sa)) 17921da177e4SLinus Torvalds goto out; 1793363168b4SJeff Layton if (unconf && !rpc_cmp_addr((struct sockaddr *) &unconf->cl_addr, sa)) 17941da177e4SLinus Torvalds goto out; 179521ab45a4SNeilBrown 1796a186e767SJ. Bruce Fields /* 1797a186e767SJ. Bruce Fields * section 14.2.34 of RFC 3530 has a description of 1798a186e767SJ. Bruce Fields * SETCLIENTID_CONFIRM request processing consisting 1799a186e767SJ. Bruce Fields * of 4 bullet points, labeled as CASE1 - CASE4 below. 1800a186e767SJ. Bruce Fields */ 1801366e0c1dSJ. Bruce Fields if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) { 1802a186e767SJ. Bruce Fields /* 1803a186e767SJ. Bruce Fields * RFC 3530 14.2.34 CASE 1: 1804a186e767SJ. Bruce Fields * callback update 18057c79f737SNeilBrown */ 1806599e0a22SJ. Bruce Fields if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) 18071da177e4SLinus Torvalds status = nfserr_clid_inuse; 18081da177e4SLinus Torvalds else { 18092bf23875SJ. Bruce Fields atomic_set(&conf->cl_cb_set, 0); 18104b21d0deSJ. Bruce Fields nfsd4_probe_callback(conf, &unconf->cl_cb_conn); 18111a69c179SNeilBrown expire_client(unconf); 18121da177e4SLinus Torvalds status = nfs_ok; 18131a69c179SNeilBrown 18141da177e4SLinus Torvalds } 1815f3aba4e5SJ. Bruce Fields } else if (conf && !unconf) { 1816a186e767SJ. Bruce Fields /* 1817a186e767SJ. Bruce Fields * RFC 3530 14.2.34 CASE 2: 1818a186e767SJ. Bruce Fields * probable retransmitted request; play it safe and 1819a186e767SJ. Bruce Fields * do nothing. 18207c79f737SNeilBrown */ 1821599e0a22SJ. Bruce Fields if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) 18221da177e4SLinus Torvalds status = nfserr_clid_inuse; 182321ab45a4SNeilBrown else 18241da177e4SLinus Torvalds status = nfs_ok; 18257c79f737SNeilBrown } else if (!conf && unconf 1826599e0a22SJ. Bruce Fields && same_verf(&unconf->cl_confirm, &confirm)) { 1827a186e767SJ. Bruce Fields /* 1828a186e767SJ. Bruce Fields * RFC 3530 14.2.34 CASE 3: 1829a186e767SJ. Bruce Fields * Normal case; new or rebooted client: 18301da177e4SLinus Torvalds */ 1831599e0a22SJ. Bruce Fields if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred)) { 18321da177e4SLinus Torvalds status = nfserr_clid_inuse; 18331da177e4SLinus Torvalds } else { 18341a69c179SNeilBrown unsigned int hash = 18351a69c179SNeilBrown clientstr_hashval(unconf->cl_recdir); 18361a69c179SNeilBrown conf = find_confirmed_client_by_str(unconf->cl_recdir, 1837a1bcecd2SAndy Adamson hash, false); 18381a69c179SNeilBrown if (conf) { 1839c7b9a459SNeilBrown nfsd4_remove_clid_dir(conf); 18401a69c179SNeilBrown expire_client(conf); 18411a69c179SNeilBrown } 18421da177e4SLinus Torvalds move_to_confirmed(unconf); 184321ab45a4SNeilBrown conf = unconf; 18444b21d0deSJ. Bruce Fields nfsd4_probe_callback(conf, &conf->cl_cb_conn); 18451a69c179SNeilBrown status = nfs_ok; 18461da177e4SLinus Torvalds } 1847599e0a22SJ. Bruce Fields } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm))) 1848599e0a22SJ. Bruce Fields && (!unconf || (unconf && !same_verf(&unconf->cl_confirm, 18497c79f737SNeilBrown &confirm)))) { 1850a186e767SJ. Bruce Fields /* 1851a186e767SJ. Bruce Fields * RFC 3530 14.2.34 CASE 4: 1852a186e767SJ. Bruce Fields * Client probably hasn't noticed that we rebooted yet. 18531da177e4SLinus Torvalds */ 18541da177e4SLinus Torvalds status = nfserr_stale_clientid; 18557c79f737SNeilBrown } else { 18561da177e4SLinus Torvalds /* check that we have hit one of the cases...*/ 185722de4d83SNeilBrown status = nfserr_clid_inuse; 185808e8987cSNeilBrown } 18591da177e4SLinus Torvalds out: 18601da177e4SLinus Torvalds nfs4_unlock_state(); 18611da177e4SLinus Torvalds return status; 18621da177e4SLinus Torvalds } 18631da177e4SLinus Torvalds 18641da177e4SLinus Torvalds /* OPEN Share state helper functions */ 18651da177e4SLinus Torvalds static inline struct nfs4_file * 18661da177e4SLinus Torvalds alloc_init_file(struct inode *ino) 18671da177e4SLinus Torvalds { 18681da177e4SLinus Torvalds struct nfs4_file *fp; 18691da177e4SLinus Torvalds unsigned int hashval = file_hashval(ino); 18701da177e4SLinus Torvalds 1871e60d4398SNeilBrown fp = kmem_cache_alloc(file_slab, GFP_KERNEL); 1872e60d4398SNeilBrown if (fp) { 18738b671b80SJ. Bruce Fields atomic_set(&fp->fi_ref, 1); 18741da177e4SLinus Torvalds INIT_LIST_HEAD(&fp->fi_hash); 18758beefa24SNeilBrown INIT_LIST_HEAD(&fp->fi_stateids); 18768beefa24SNeilBrown INIT_LIST_HEAD(&fp->fi_delegations); 18771da177e4SLinus Torvalds fp->fi_inode = igrab(ino); 18781da177e4SLinus Torvalds fp->fi_id = current_fileid++; 187947f9940cSMeelap Shah fp->fi_had_conflict = false; 1880f9d7562fSJ. Bruce Fields memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); 1881f9d7562fSJ. Bruce Fields memset(fp->fi_access, 0, sizeof(fp->fi_access)); 188247cee541SPavel Emelyanov spin_lock(&recall_lock); 188347cee541SPavel Emelyanov list_add(&fp->fi_hash, &file_hashtbl[hashval]); 188447cee541SPavel Emelyanov spin_unlock(&recall_lock); 18851da177e4SLinus Torvalds return fp; 18861da177e4SLinus Torvalds } 18871da177e4SLinus Torvalds return NULL; 18881da177e4SLinus Torvalds } 18891da177e4SLinus Torvalds 18901da177e4SLinus Torvalds static void 1891e18b890bSChristoph Lameter nfsd4_free_slab(struct kmem_cache **slab) 1892e60d4398SNeilBrown { 1893e60d4398SNeilBrown if (*slab == NULL) 1894e60d4398SNeilBrown return; 18951a1d92c1SAlexey Dobriyan kmem_cache_destroy(*slab); 1896e60d4398SNeilBrown *slab = NULL; 1897e60d4398SNeilBrown } 1898e60d4398SNeilBrown 1899e8ff2a84SJ. Bruce Fields void 1900e60d4398SNeilBrown nfsd4_free_slabs(void) 1901e60d4398SNeilBrown { 1902e60d4398SNeilBrown nfsd4_free_slab(&stateowner_slab); 1903e60d4398SNeilBrown nfsd4_free_slab(&file_slab); 19045ac049acSNeilBrown nfsd4_free_slab(&stateid_slab); 19055b2d21c1SNeilBrown nfsd4_free_slab(&deleg_slab); 1906e60d4398SNeilBrown } 19071da177e4SLinus Torvalds 19081da177e4SLinus Torvalds static int 19091da177e4SLinus Torvalds nfsd4_init_slabs(void) 19101da177e4SLinus Torvalds { 19111da177e4SLinus Torvalds stateowner_slab = kmem_cache_create("nfsd4_stateowners", 191220c2df83SPaul Mundt sizeof(struct nfs4_stateowner), 0, 0, NULL); 1913e60d4398SNeilBrown if (stateowner_slab == NULL) 1914e60d4398SNeilBrown goto out_nomem; 1915e60d4398SNeilBrown file_slab = kmem_cache_create("nfsd4_files", 191620c2df83SPaul Mundt sizeof(struct nfs4_file), 0, 0, NULL); 1917e60d4398SNeilBrown if (file_slab == NULL) 1918e60d4398SNeilBrown goto out_nomem; 19195ac049acSNeilBrown stateid_slab = kmem_cache_create("nfsd4_stateids", 192020c2df83SPaul Mundt sizeof(struct nfs4_stateid), 0, 0, NULL); 19215ac049acSNeilBrown if (stateid_slab == NULL) 19225ac049acSNeilBrown goto out_nomem; 19235b2d21c1SNeilBrown deleg_slab = kmem_cache_create("nfsd4_delegations", 192420c2df83SPaul Mundt sizeof(struct nfs4_delegation), 0, 0, NULL); 19255b2d21c1SNeilBrown if (deleg_slab == NULL) 19265b2d21c1SNeilBrown goto out_nomem; 1927e60d4398SNeilBrown return 0; 1928e60d4398SNeilBrown out_nomem: 1929e60d4398SNeilBrown nfsd4_free_slabs(); 19301da177e4SLinus Torvalds dprintk("nfsd4: out of memory while initializing nfsv4\n"); 19311da177e4SLinus Torvalds return -ENOMEM; 19321da177e4SLinus Torvalds } 19331da177e4SLinus Torvalds 19341da177e4SLinus Torvalds void 19351da177e4SLinus Torvalds nfs4_free_stateowner(struct kref *kref) 19361da177e4SLinus Torvalds { 19371da177e4SLinus Torvalds struct nfs4_stateowner *sop = 19381da177e4SLinus Torvalds container_of(kref, struct nfs4_stateowner, so_ref); 19391da177e4SLinus Torvalds kfree(sop->so_owner.data); 19401da177e4SLinus Torvalds kmem_cache_free(stateowner_slab, sop); 19411da177e4SLinus Torvalds } 19421da177e4SLinus Torvalds 19431da177e4SLinus Torvalds static inline struct nfs4_stateowner * 19441da177e4SLinus Torvalds alloc_stateowner(struct xdr_netobj *owner) 19451da177e4SLinus Torvalds { 19461da177e4SLinus Torvalds struct nfs4_stateowner *sop; 19471da177e4SLinus Torvalds 19481da177e4SLinus Torvalds if ((sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL))) { 19491da177e4SLinus Torvalds if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { 19501da177e4SLinus Torvalds memcpy(sop->so_owner.data, owner->data, owner->len); 19511da177e4SLinus Torvalds sop->so_owner.len = owner->len; 19521da177e4SLinus Torvalds kref_init(&sop->so_ref); 19531da177e4SLinus Torvalds return sop; 19541da177e4SLinus Torvalds } 19551da177e4SLinus Torvalds kmem_cache_free(stateowner_slab, sop); 19561da177e4SLinus Torvalds } 19571da177e4SLinus Torvalds return NULL; 19581da177e4SLinus Torvalds } 19591da177e4SLinus Torvalds 19601da177e4SLinus Torvalds static struct nfs4_stateowner * 19611da177e4SLinus Torvalds alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) { 19621da177e4SLinus Torvalds struct nfs4_stateowner *sop; 19631da177e4SLinus Torvalds struct nfs4_replay *rp; 19641da177e4SLinus Torvalds unsigned int idhashval; 19651da177e4SLinus Torvalds 19661da177e4SLinus Torvalds if (!(sop = alloc_stateowner(&open->op_owner))) 19671da177e4SLinus Torvalds return NULL; 19681da177e4SLinus Torvalds idhashval = ownerid_hashval(current_ownerid); 19691da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_idhash); 19701da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_strhash); 19711da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_perclient); 1972ea1da636SNeilBrown INIT_LIST_HEAD(&sop->so_stateids); 1973ea1da636SNeilBrown INIT_LIST_HEAD(&sop->so_perstateid); /* not used */ 19741da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_close_lru); 19751da177e4SLinus Torvalds sop->so_time = 0; 19761da177e4SLinus Torvalds list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); 19771da177e4SLinus Torvalds list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); 1978ea1da636SNeilBrown list_add(&sop->so_perclient, &clp->cl_openowners); 19791da177e4SLinus Torvalds sop->so_is_open_owner = 1; 19801da177e4SLinus Torvalds sop->so_id = current_ownerid++; 19811da177e4SLinus Torvalds sop->so_client = clp; 19821da177e4SLinus Torvalds sop->so_seqid = open->op_seqid; 19831da177e4SLinus Torvalds sop->so_confirmed = 0; 19841da177e4SLinus Torvalds rp = &sop->so_replay; 1985de1ae286SAl Viro rp->rp_status = nfserr_serverfault; 19861da177e4SLinus Torvalds rp->rp_buflen = 0; 19871da177e4SLinus Torvalds rp->rp_buf = rp->rp_ibuf; 19881da177e4SLinus Torvalds return sop; 19891da177e4SLinus Torvalds } 19901da177e4SLinus Torvalds 19911da177e4SLinus Torvalds static inline void 19921da177e4SLinus Torvalds init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { 19931da177e4SLinus Torvalds struct nfs4_stateowner *sop = open->op_stateowner; 19941da177e4SLinus Torvalds unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds INIT_LIST_HEAD(&stp->st_hash); 1997ea1da636SNeilBrown INIT_LIST_HEAD(&stp->st_perstateowner); 1998ea1da636SNeilBrown INIT_LIST_HEAD(&stp->st_lockowners); 19991da177e4SLinus Torvalds INIT_LIST_HEAD(&stp->st_perfile); 20001da177e4SLinus Torvalds list_add(&stp->st_hash, &stateid_hashtbl[hashval]); 2001ea1da636SNeilBrown list_add(&stp->st_perstateowner, &sop->so_stateids); 20028beefa24SNeilBrown list_add(&stp->st_perfile, &fp->fi_stateids); 20031da177e4SLinus Torvalds stp->st_stateowner = sop; 200413cd2184SNeilBrown get_nfs4_file(fp); 20051da177e4SLinus Torvalds stp->st_file = fp; 2006e4e83ea4SJ. Bruce Fields stp->st_stateid.si_boot = boot_time; 20071da177e4SLinus Torvalds stp->st_stateid.si_stateownerid = sop->so_id; 20081da177e4SLinus Torvalds stp->st_stateid.si_fileid = fp->fi_id; 20091da177e4SLinus Torvalds stp->st_stateid.si_generation = 0; 20101da177e4SLinus Torvalds stp->st_access_bmap = 0; 20111da177e4SLinus Torvalds stp->st_deny_bmap = 0; 201284459a11SAndy Adamson __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK, 201384459a11SAndy Adamson &stp->st_access_bmap); 20141da177e4SLinus Torvalds __set_bit(open->op_share_deny, &stp->st_deny_bmap); 20154c4cd222SNeilBrown stp->st_openstp = NULL; 20161da177e4SLinus Torvalds } 20171da177e4SLinus Torvalds 20181da177e4SLinus Torvalds static void 20191da177e4SLinus Torvalds move_to_close_lru(struct nfs4_stateowner *sop) 20201da177e4SLinus Torvalds { 20211da177e4SLinus Torvalds dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); 20221da177e4SLinus Torvalds 2023358dd55aSNeilBrown list_move_tail(&sop->so_close_lru, &close_lru); 20241da177e4SLinus Torvalds sop->so_time = get_seconds(); 20251da177e4SLinus Torvalds } 20261da177e4SLinus Torvalds 20271da177e4SLinus Torvalds static int 2028599e0a22SJ. Bruce Fields same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, 2029599e0a22SJ. Bruce Fields clientid_t *clid) 2030599e0a22SJ. Bruce Fields { 2031599e0a22SJ. Bruce Fields return (sop->so_owner.len == owner->len) && 2032599e0a22SJ. Bruce Fields 0 == memcmp(sop->so_owner.data, owner->data, owner->len) && 2033599e0a22SJ. Bruce Fields (sop->so_client->cl_clientid.cl_id == clid->cl_id); 20341da177e4SLinus Torvalds } 20351da177e4SLinus Torvalds 20361da177e4SLinus Torvalds static struct nfs4_stateowner * 20371da177e4SLinus Torvalds find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) 20381da177e4SLinus Torvalds { 20391da177e4SLinus Torvalds struct nfs4_stateowner *so = NULL; 20401da177e4SLinus Torvalds 20411da177e4SLinus Torvalds list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { 2042599e0a22SJ. Bruce Fields if (same_owner_str(so, &open->op_owner, &open->op_clientid)) 20431da177e4SLinus Torvalds return so; 20441da177e4SLinus Torvalds } 20451da177e4SLinus Torvalds return NULL; 20461da177e4SLinus Torvalds } 20471da177e4SLinus Torvalds 20481da177e4SLinus Torvalds /* search file_hashtbl[] for file */ 20491da177e4SLinus Torvalds static struct nfs4_file * 20501da177e4SLinus Torvalds find_file(struct inode *ino) 20511da177e4SLinus Torvalds { 20521da177e4SLinus Torvalds unsigned int hashval = file_hashval(ino); 20531da177e4SLinus Torvalds struct nfs4_file *fp; 20541da177e4SLinus Torvalds 20558b671b80SJ. Bruce Fields spin_lock(&recall_lock); 20561da177e4SLinus Torvalds list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { 205713cd2184SNeilBrown if (fp->fi_inode == ino) { 205813cd2184SNeilBrown get_nfs4_file(fp); 20598b671b80SJ. Bruce Fields spin_unlock(&recall_lock); 20601da177e4SLinus Torvalds return fp; 20611da177e4SLinus Torvalds } 206213cd2184SNeilBrown } 20638b671b80SJ. Bruce Fields spin_unlock(&recall_lock); 20641da177e4SLinus Torvalds return NULL; 20651da177e4SLinus Torvalds } 20661da177e4SLinus Torvalds 2067d87a8adeSAndy Adamson static inline int access_valid(u32 x, u32 minorversion) 2068ba5a6a19SJ. Bruce Fields { 2069d87a8adeSAndy Adamson if ((x & NFS4_SHARE_ACCESS_MASK) < NFS4_SHARE_ACCESS_READ) 20708838dc43SJ. Bruce Fields return 0; 2071d87a8adeSAndy Adamson if ((x & NFS4_SHARE_ACCESS_MASK) > NFS4_SHARE_ACCESS_BOTH) 2072d87a8adeSAndy Adamson return 0; 2073d87a8adeSAndy Adamson x &= ~NFS4_SHARE_ACCESS_MASK; 2074d87a8adeSAndy Adamson if (minorversion && x) { 2075d87a8adeSAndy Adamson if ((x & NFS4_SHARE_WANT_MASK) > NFS4_SHARE_WANT_CANCEL) 2076d87a8adeSAndy Adamson return 0; 2077d87a8adeSAndy Adamson if ((x & NFS4_SHARE_WHEN_MASK) > NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED) 2078d87a8adeSAndy Adamson return 0; 2079d87a8adeSAndy Adamson x &= ~(NFS4_SHARE_WANT_MASK | NFS4_SHARE_WHEN_MASK); 2080d87a8adeSAndy Adamson } 2081d87a8adeSAndy Adamson if (x) 20828838dc43SJ. Bruce Fields return 0; 20838838dc43SJ. Bruce Fields return 1; 2084ba5a6a19SJ. Bruce Fields } 2085ba5a6a19SJ. Bruce Fields 20868838dc43SJ. Bruce Fields static inline int deny_valid(u32 x) 2087ba5a6a19SJ. Bruce Fields { 20888838dc43SJ. Bruce Fields /* Note: unlike access bits, deny bits may be zero. */ 20898838dc43SJ. Bruce Fields return x <= NFS4_SHARE_DENY_BOTH; 2090ba5a6a19SJ. Bruce Fields } 20911da177e4SLinus Torvalds 20924f83aa30SJ. Bruce Fields /* 20931da177e4SLinus Torvalds * Called to check deny when READ with all zero stateid or 20941da177e4SLinus Torvalds * WRITE with all zero or all one stateid 20951da177e4SLinus Torvalds */ 2096b37ad28bSAl Viro static __be32 20971da177e4SLinus Torvalds nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) 20981da177e4SLinus Torvalds { 20991da177e4SLinus Torvalds struct inode *ino = current_fh->fh_dentry->d_inode; 21001da177e4SLinus Torvalds struct nfs4_file *fp; 21011da177e4SLinus Torvalds struct nfs4_stateid *stp; 2102b37ad28bSAl Viro __be32 ret; 21031da177e4SLinus Torvalds 21041da177e4SLinus Torvalds dprintk("NFSD: nfs4_share_conflict\n"); 21051da177e4SLinus Torvalds 21061da177e4SLinus Torvalds fp = find_file(ino); 210713cd2184SNeilBrown if (!fp) 210813cd2184SNeilBrown return nfs_ok; 2109b700949bSNeilBrown ret = nfserr_locked; 21101da177e4SLinus Torvalds /* Search for conflicting share reservations */ 21118beefa24SNeilBrown list_for_each_entry(stp, &fp->fi_stateids, st_perfile) { 21121da177e4SLinus Torvalds if (test_bit(deny_type, &stp->st_deny_bmap) || 21131da177e4SLinus Torvalds test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) 211413cd2184SNeilBrown goto out; 21151da177e4SLinus Torvalds } 211613cd2184SNeilBrown ret = nfs_ok; 211713cd2184SNeilBrown out: 211813cd2184SNeilBrown put_nfs4_file(fp); 211913cd2184SNeilBrown return ret; 21201da177e4SLinus Torvalds } 21211da177e4SLinus Torvalds 21221da177e4SLinus Torvalds static inline void 2123f9d7562fSJ. Bruce Fields nfs4_file_downgrade(struct nfs4_file *fp, unsigned int share_access) 21241da177e4SLinus Torvalds { 2125f9d7562fSJ. Bruce Fields if (share_access & NFS4_SHARE_ACCESS_WRITE) 2126f9d7562fSJ. Bruce Fields nfs4_file_put_access(fp, O_WRONLY); 2127f9d7562fSJ. Bruce Fields if (share_access & NFS4_SHARE_ACCESS_READ) 2128f9d7562fSJ. Bruce Fields nfs4_file_put_access(fp, O_RDONLY); 21291da177e4SLinus Torvalds } 21301da177e4SLinus Torvalds 21311da177e4SLinus Torvalds /* 21321da177e4SLinus Torvalds * Spawn a thread to perform a recall on the delegation represented 21331da177e4SLinus Torvalds * by the lease (file_lock) 21341da177e4SLinus Torvalds * 21351da177e4SLinus Torvalds * Called from break_lease() with lock_kernel() held. 21361da177e4SLinus Torvalds * Note: we assume break_lease will only call this *once* for any given 21371da177e4SLinus Torvalds * lease. 21381da177e4SLinus Torvalds */ 21391da177e4SLinus Torvalds static 21401da177e4SLinus Torvalds void nfsd_break_deleg_cb(struct file_lock *fl) 21411da177e4SLinus Torvalds { 21421da177e4SLinus Torvalds struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; 21431da177e4SLinus Torvalds 21441da177e4SLinus Torvalds dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl); 21451da177e4SLinus Torvalds if (!dp) 21461da177e4SLinus Torvalds return; 21471da177e4SLinus Torvalds 21481da177e4SLinus Torvalds /* We're assuming the state code never drops its reference 21491da177e4SLinus Torvalds * without first removing the lease. Since we're in this lease 21501da177e4SLinus Torvalds * callback (and since the lease code is serialized by the kernel 21511da177e4SLinus Torvalds * lock) we know the server hasn't removed the lease yet, we know 21521da177e4SLinus Torvalds * it's safe to take a reference: */ 21531da177e4SLinus Torvalds atomic_inc(&dp->dl_count); 21541da177e4SLinus Torvalds 21551da177e4SLinus Torvalds spin_lock(&recall_lock); 21561da177e4SLinus Torvalds list_add_tail(&dp->dl_recall_lru, &del_recall_lru); 21571da177e4SLinus Torvalds spin_unlock(&recall_lock); 21581da177e4SLinus Torvalds 21591da177e4SLinus Torvalds /* only place dl_time is set. protected by lock_kernel*/ 21601da177e4SLinus Torvalds dp->dl_time = get_seconds(); 21611da177e4SLinus Torvalds 21620272e1fdSJ. Bruce Fields /* 21630272e1fdSJ. Bruce Fields * We don't want the locks code to timeout the lease for us; 21640272e1fdSJ. Bruce Fields * we'll remove it ourself if the delegation isn't returned 21650272e1fdSJ. Bruce Fields * in time. 21660272e1fdSJ. Bruce Fields */ 21670272e1fdSJ. Bruce Fields fl->fl_break_time = 0; 21681da177e4SLinus Torvalds 216963e4863fSJ. Bruce Fields dp->dl_file->fi_had_conflict = true; 217063e4863fSJ. Bruce Fields nfsd4_cb_recall(dp); 21711da177e4SLinus Torvalds } 21721da177e4SLinus Torvalds 21731da177e4SLinus Torvalds /* 21741da177e4SLinus Torvalds * The file_lock is being reapd. 21751da177e4SLinus Torvalds * 21761da177e4SLinus Torvalds * Called by locks_free_lock() with lock_kernel() held. 21771da177e4SLinus Torvalds */ 21781da177e4SLinus Torvalds static 21791da177e4SLinus Torvalds void nfsd_release_deleg_cb(struct file_lock *fl) 21801da177e4SLinus Torvalds { 21811da177e4SLinus Torvalds struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; 21821da177e4SLinus Torvalds 21831da177e4SLinus Torvalds dprintk("NFSD nfsd_release_deleg_cb: fl %p dp %p dl_count %d\n", fl,dp, atomic_read(&dp->dl_count)); 21841da177e4SLinus Torvalds 21851da177e4SLinus Torvalds if (!(fl->fl_flags & FL_LEASE) || !dp) 21861da177e4SLinus Torvalds return; 21871da177e4SLinus Torvalds dp->dl_flock = NULL; 21881da177e4SLinus Torvalds } 21891da177e4SLinus Torvalds 21901da177e4SLinus Torvalds /* 21911da177e4SLinus Torvalds * Set the delegation file_lock back pointer. 21921da177e4SLinus Torvalds * 2193a9933ceaSJ. Bruce Fields * Called from setlease() with lock_kernel() held. 21941da177e4SLinus Torvalds */ 21951da177e4SLinus Torvalds static 21961da177e4SLinus Torvalds void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) 21971da177e4SLinus Torvalds { 21981da177e4SLinus Torvalds struct nfs4_delegation *dp = (struct nfs4_delegation *)new->fl_owner; 21991da177e4SLinus Torvalds 22001da177e4SLinus Torvalds dprintk("NFSD: nfsd_copy_lock_deleg_cb: new fl %p dp %p\n", new, dp); 22011da177e4SLinus Torvalds if (!dp) 22021da177e4SLinus Torvalds return; 22031da177e4SLinus Torvalds dp->dl_flock = new; 22041da177e4SLinus Torvalds } 22051da177e4SLinus Torvalds 22061da177e4SLinus Torvalds /* 2207a9933ceaSJ. Bruce Fields * Called from setlease() with lock_kernel() held 22081da177e4SLinus Torvalds */ 22091da177e4SLinus Torvalds static 22101da177e4SLinus Torvalds int nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try) 22111da177e4SLinus Torvalds { 22121da177e4SLinus Torvalds struct nfs4_delegation *onlistd = 22131da177e4SLinus Torvalds (struct nfs4_delegation *)onlist->fl_owner; 22141da177e4SLinus Torvalds struct nfs4_delegation *tryd = 22151da177e4SLinus Torvalds (struct nfs4_delegation *)try->fl_owner; 22161da177e4SLinus Torvalds 22171da177e4SLinus Torvalds if (onlist->fl_lmops != try->fl_lmops) 22181da177e4SLinus Torvalds return 0; 22191da177e4SLinus Torvalds 22201da177e4SLinus Torvalds return onlistd->dl_client == tryd->dl_client; 22211da177e4SLinus Torvalds } 22221da177e4SLinus Torvalds 22231da177e4SLinus Torvalds 22241da177e4SLinus Torvalds static 22251da177e4SLinus Torvalds int nfsd_change_deleg_cb(struct file_lock **onlist, int arg) 22261da177e4SLinus Torvalds { 22271da177e4SLinus Torvalds if (arg & F_UNLCK) 22281da177e4SLinus Torvalds return lease_modify(onlist, arg); 22291da177e4SLinus Torvalds else 22301da177e4SLinus Torvalds return -EAGAIN; 22311da177e4SLinus Torvalds } 22321da177e4SLinus Torvalds 22337b021967SAlexey Dobriyan static const struct lock_manager_operations nfsd_lease_mng_ops = { 22341da177e4SLinus Torvalds .fl_break = nfsd_break_deleg_cb, 22351da177e4SLinus Torvalds .fl_release_private = nfsd_release_deleg_cb, 22361da177e4SLinus Torvalds .fl_copy_lock = nfsd_copy_lock_deleg_cb, 22371da177e4SLinus Torvalds .fl_mylease = nfsd_same_client_deleg_cb, 22381da177e4SLinus Torvalds .fl_change = nfsd_change_deleg_cb, 22391da177e4SLinus Torvalds }; 22401da177e4SLinus Torvalds 22411da177e4SLinus Torvalds 2242b37ad28bSAl Viro __be32 22436668958fSAndy Adamson nfsd4_process_open1(struct nfsd4_compound_state *cstate, 22446668958fSAndy Adamson struct nfsd4_open *open) 22451da177e4SLinus Torvalds { 22461da177e4SLinus Torvalds clientid_t *clientid = &open->op_clientid; 22471da177e4SLinus Torvalds struct nfs4_client *clp = NULL; 22481da177e4SLinus Torvalds unsigned int strhashval; 22491da177e4SLinus Torvalds struct nfs4_stateowner *sop = NULL; 22501da177e4SLinus Torvalds 22511da177e4SLinus Torvalds if (!check_name(open->op_owner)) 22520f442aa2SJ. Bruce Fields return nfserr_inval; 22531da177e4SLinus Torvalds 22541da177e4SLinus Torvalds if (STALE_CLIENTID(&open->op_clientid)) 22551da177e4SLinus Torvalds return nfserr_stale_clientid; 22561da177e4SLinus Torvalds 22571da177e4SLinus Torvalds strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner); 22581da177e4SLinus Torvalds sop = find_openstateowner_str(strhashval, open); 22591da177e4SLinus Torvalds open->op_stateowner = sop; 22600f442aa2SJ. Bruce Fields if (!sop) { 22610f442aa2SJ. Bruce Fields /* Make sure the client's lease hasn't expired. */ 22620f442aa2SJ. Bruce Fields clp = find_confirmed_client(clientid); 22630f442aa2SJ. Bruce Fields if (clp == NULL) 22640f442aa2SJ. Bruce Fields return nfserr_expired; 22650f442aa2SJ. Bruce Fields goto renew; 22660f442aa2SJ. Bruce Fields } 22676668958fSAndy Adamson /* When sessions are used, skip open sequenceid processing */ 22686668958fSAndy Adamson if (nfsd4_has_session(cstate)) 22696668958fSAndy Adamson goto renew; 2270ae8b6253SJ. Bruce Fields if (!sop->so_confirmed) { 22710f442aa2SJ. Bruce Fields /* Replace unconfirmed owners without checking for replay. */ 2272ae8b6253SJ. Bruce Fields clp = sop->so_client; 2273f044ff83SJ. Bruce Fields release_openowner(sop); 22740f442aa2SJ. Bruce Fields open->op_stateowner = NULL; 2275ae8b6253SJ. Bruce Fields goto renew; 22760f442aa2SJ. Bruce Fields } 22770f442aa2SJ. Bruce Fields if (open->op_seqid == sop->so_seqid - 1) { 22781da177e4SLinus Torvalds if (sop->so_replay.rp_buflen) 2279a90b061cSAl Viro return nfserr_replay_me; 22801da177e4SLinus Torvalds /* The original OPEN failed so spectacularly 22811da177e4SLinus Torvalds * that we don't even have replay data saved! 22821da177e4SLinus Torvalds * Therefore, we have no choice but to continue 22831da177e4SLinus Torvalds * processing this OPEN; presumably, we'll 22841da177e4SLinus Torvalds * fail again for the same reason. 22851da177e4SLinus Torvalds */ 22860f442aa2SJ. Bruce Fields dprintk("nfsd4_process_open1: replay with no replay cache\n"); 22871da177e4SLinus Torvalds goto renew; 22881da177e4SLinus Torvalds } 22890f442aa2SJ. Bruce Fields if (open->op_seqid != sop->so_seqid) 22900f442aa2SJ. Bruce Fields return nfserr_bad_seqid; 22910f442aa2SJ. Bruce Fields renew: 22920f442aa2SJ. Bruce Fields if (open->op_stateowner == NULL) { 22931da177e4SLinus Torvalds sop = alloc_init_open_stateowner(strhashval, clp, open); 22941da177e4SLinus Torvalds if (sop == NULL) 22950f442aa2SJ. Bruce Fields return nfserr_resource; 22961da177e4SLinus Torvalds open->op_stateowner = sop; 22970f442aa2SJ. Bruce Fields } 22980f442aa2SJ. Bruce Fields list_del_init(&sop->so_close_lru); 22991da177e4SLinus Torvalds renew_client(sop->so_client); 23000f442aa2SJ. Bruce Fields return nfs_ok; 23011da177e4SLinus Torvalds } 23021da177e4SLinus Torvalds 2303b37ad28bSAl Viro static inline __be32 23044a6e43e6SNeilBrown nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) 23054a6e43e6SNeilBrown { 23064a6e43e6SNeilBrown if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ)) 23074a6e43e6SNeilBrown return nfserr_openmode; 23084a6e43e6SNeilBrown else 23094a6e43e6SNeilBrown return nfs_ok; 23104a6e43e6SNeilBrown } 23114a6e43e6SNeilBrown 231252f4fb43SNeilBrown static struct nfs4_delegation * 231352f4fb43SNeilBrown find_delegation_file(struct nfs4_file *fp, stateid_t *stid) 231452f4fb43SNeilBrown { 231552f4fb43SNeilBrown struct nfs4_delegation *dp; 231652f4fb43SNeilBrown 2317ea1da636SNeilBrown list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) { 231852f4fb43SNeilBrown if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) 231952f4fb43SNeilBrown return dp; 232052f4fb43SNeilBrown } 232152f4fb43SNeilBrown return NULL; 232252f4fb43SNeilBrown } 232352f4fb43SNeilBrown 232424a0111eSJ. Bruce Fields int share_access_to_flags(u32 share_access) 232524a0111eSJ. Bruce Fields { 232624a0111eSJ. Bruce Fields share_access &= ~NFS4_SHARE_WANT_MASK; 232724a0111eSJ. Bruce Fields 232824a0111eSJ. Bruce Fields return share_access == NFS4_SHARE_ACCESS_READ ? RD_STATE : WR_STATE; 232924a0111eSJ. Bruce Fields } 233024a0111eSJ. Bruce Fields 2331b37ad28bSAl Viro static __be32 2332567d9829SNeilBrown nfs4_check_deleg(struct nfs4_file *fp, struct nfsd4_open *open, 2333567d9829SNeilBrown struct nfs4_delegation **dp) 2334567d9829SNeilBrown { 2335567d9829SNeilBrown int flags; 2336b37ad28bSAl Viro __be32 status = nfserr_bad_stateid; 2337567d9829SNeilBrown 2338567d9829SNeilBrown *dp = find_delegation_file(fp, &open->op_delegate_stateid); 2339567d9829SNeilBrown if (*dp == NULL) 2340c44c5eebSNeilBrown goto out; 234124a0111eSJ. Bruce Fields flags = share_access_to_flags(open->op_share_access); 2342567d9829SNeilBrown status = nfs4_check_delegmode(*dp, flags); 2343567d9829SNeilBrown if (status) 2344567d9829SNeilBrown *dp = NULL; 2345c44c5eebSNeilBrown out: 2346c44c5eebSNeilBrown if (open->op_claim_type != NFS4_OPEN_CLAIM_DELEGATE_CUR) 2347c44c5eebSNeilBrown return nfs_ok; 2348c44c5eebSNeilBrown if (status) 2349c44c5eebSNeilBrown return status; 2350c44c5eebSNeilBrown open->op_stateowner->so_confirmed = 1; 2351c44c5eebSNeilBrown return nfs_ok; 2352567d9829SNeilBrown } 2353567d9829SNeilBrown 2354b37ad28bSAl Viro static __be32 23551da177e4SLinus Torvalds nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) 23561da177e4SLinus Torvalds { 23571da177e4SLinus Torvalds struct nfs4_stateid *local; 2358b37ad28bSAl Viro __be32 status = nfserr_share_denied; 23591da177e4SLinus Torvalds struct nfs4_stateowner *sop = open->op_stateowner; 23601da177e4SLinus Torvalds 23618beefa24SNeilBrown list_for_each_entry(local, &fp->fi_stateids, st_perfile) { 23621da177e4SLinus Torvalds /* ignore lock owners */ 23631da177e4SLinus Torvalds if (local->st_stateowner->so_is_open_owner == 0) 23641da177e4SLinus Torvalds continue; 23651da177e4SLinus Torvalds /* remember if we have seen this open owner */ 23661da177e4SLinus Torvalds if (local->st_stateowner == sop) 23671da177e4SLinus Torvalds *stpp = local; 23681da177e4SLinus Torvalds /* check for conflicting share reservations */ 23691da177e4SLinus Torvalds if (!test_share(local, open)) 23701da177e4SLinus Torvalds goto out; 23711da177e4SLinus Torvalds } 23721da177e4SLinus Torvalds status = 0; 23731da177e4SLinus Torvalds out: 23741da177e4SLinus Torvalds return status; 23751da177e4SLinus Torvalds } 23761da177e4SLinus Torvalds 23775ac049acSNeilBrown static inline struct nfs4_stateid * 23785ac049acSNeilBrown nfs4_alloc_stateid(void) 23795ac049acSNeilBrown { 23805ac049acSNeilBrown return kmem_cache_alloc(stateid_slab, GFP_KERNEL); 23815ac049acSNeilBrown } 23825ac049acSNeilBrown 238321fb4016SJ. Bruce Fields static inline int nfs4_access_to_access(u32 nfs4_access) 238421fb4016SJ. Bruce Fields { 238521fb4016SJ. Bruce Fields int flags = 0; 238621fb4016SJ. Bruce Fields 238721fb4016SJ. Bruce Fields if (nfs4_access & NFS4_SHARE_ACCESS_READ) 238821fb4016SJ. Bruce Fields flags |= NFSD_MAY_READ; 238921fb4016SJ. Bruce Fields if (nfs4_access & NFS4_SHARE_ACCESS_WRITE) 239021fb4016SJ. Bruce Fields flags |= NFSD_MAY_WRITE; 239121fb4016SJ. Bruce Fields return flags; 239221fb4016SJ. Bruce Fields } 239321fb4016SJ. Bruce Fields 2394f9d7562fSJ. Bruce Fields static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file 2395f9d7562fSJ. Bruce Fields *fp, struct svc_fh *cur_fh, u32 nfs4_access) 2396f9d7562fSJ. Bruce Fields { 2397f9d7562fSJ. Bruce Fields __be32 status; 2398f9d7562fSJ. Bruce Fields int oflag = nfs4_access_to_omode(nfs4_access); 2399f9d7562fSJ. Bruce Fields int access = nfs4_access_to_access(nfs4_access); 2400f9d7562fSJ. Bruce Fields 2401f9d7562fSJ. Bruce Fields if (!fp->fi_fds[oflag]) { 2402f9d7562fSJ. Bruce Fields status = nfsd_open(rqstp, cur_fh, S_IFREG, access, 2403f9d7562fSJ. Bruce Fields &fp->fi_fds[oflag]); 2404f9d7562fSJ. Bruce Fields if (status == nfserr_dropit) 2405f9d7562fSJ. Bruce Fields status = nfserr_jukebox; 2406f9d7562fSJ. Bruce Fields if (status) 2407f9d7562fSJ. Bruce Fields return status; 2408f9d7562fSJ. Bruce Fields } 2409f9d7562fSJ. Bruce Fields nfs4_file_get_access(fp, oflag); 2410f9d7562fSJ. Bruce Fields 2411f9d7562fSJ. Bruce Fields return nfs_ok; 2412f9d7562fSJ. Bruce Fields } 2413f9d7562fSJ. Bruce Fields 2414b37ad28bSAl Viro static __be32 24151da177e4SLinus Torvalds nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, 2416f9d7562fSJ. Bruce Fields struct nfs4_file *fp, struct svc_fh *cur_fh, 2417f9d7562fSJ. Bruce Fields struct nfsd4_open *open) 24181da177e4SLinus Torvalds { 24191da177e4SLinus Torvalds struct nfs4_stateid *stp; 2420f9d7562fSJ. Bruce Fields __be32 status; 24211da177e4SLinus Torvalds 24225ac049acSNeilBrown stp = nfs4_alloc_stateid(); 24231da177e4SLinus Torvalds if (stp == NULL) 24241da177e4SLinus Torvalds return nfserr_resource; 24251da177e4SLinus Torvalds 2426f9d7562fSJ. Bruce Fields status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open->op_share_access); 24271da177e4SLinus Torvalds if (status) { 24285ac049acSNeilBrown kmem_cache_free(stateid_slab, stp); 24291da177e4SLinus Torvalds return status; 24301da177e4SLinus Torvalds } 24311da177e4SLinus Torvalds *stpp = stp; 24321da177e4SLinus Torvalds return 0; 24331da177e4SLinus Torvalds } 24341da177e4SLinus Torvalds 2435b37ad28bSAl Viro static inline __be32 24361da177e4SLinus Torvalds nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, 24371da177e4SLinus Torvalds struct nfsd4_open *open) 24381da177e4SLinus Torvalds { 24391da177e4SLinus Torvalds struct iattr iattr = { 24401da177e4SLinus Torvalds .ia_valid = ATTR_SIZE, 24411da177e4SLinus Torvalds .ia_size = 0, 24421da177e4SLinus Torvalds }; 24431da177e4SLinus Torvalds if (!open->op_truncate) 24441da177e4SLinus Torvalds return 0; 24451da177e4SLinus Torvalds if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) 24469246585aSAl Viro return nfserr_inval; 24471da177e4SLinus Torvalds return nfsd_setattr(rqstp, fh, &iattr, 0, (time_t)0); 24481da177e4SLinus Torvalds } 24491da177e4SLinus Torvalds 2450b37ad28bSAl Viro static __be32 2451f9d7562fSJ. Bruce Fields nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_stateid *stp, struct nfsd4_open *open) 24521da177e4SLinus Torvalds { 24537d947842SJ. Bruce Fields u32 op_share_access = open->op_share_access & ~NFS4_SHARE_WANT_MASK; 24547d947842SJ. Bruce Fields bool new_access; 2455b37ad28bSAl Viro __be32 status; 24561da177e4SLinus Torvalds 24577d947842SJ. Bruce Fields new_access = !test_bit(op_share_access, &stp->st_access_bmap); 2458f9d7562fSJ. Bruce Fields if (new_access) { 24597d947842SJ. Bruce Fields status = nfs4_get_vfs_file(rqstp, fp, cur_fh, op_share_access); 2460f9d7562fSJ. Bruce Fields if (status) 2461f9d7562fSJ. Bruce Fields return status; 24626c26d08fSJ. Bruce Fields } 24631da177e4SLinus Torvalds status = nfsd4_truncate(rqstp, cur_fh, open); 24641da177e4SLinus Torvalds if (status) { 2465f9d7562fSJ. Bruce Fields if (new_access) { 2466f9d7562fSJ. Bruce Fields int oflag = nfs4_access_to_omode(new_access); 2467f9d7562fSJ. Bruce Fields nfs4_file_put_access(fp, oflag); 2468f9d7562fSJ. Bruce Fields } 24691da177e4SLinus Torvalds return status; 24701da177e4SLinus Torvalds } 24711da177e4SLinus Torvalds /* remember the open */ 247224a0111eSJ. Bruce Fields __set_bit(op_share_access, &stp->st_access_bmap); 2473b55e0ba1SJ. Bruce Fields __set_bit(open->op_share_deny, &stp->st_deny_bmap); 24741da177e4SLinus Torvalds 24751da177e4SLinus Torvalds return nfs_ok; 24761da177e4SLinus Torvalds } 24771da177e4SLinus Torvalds 24781da177e4SLinus Torvalds 24791da177e4SLinus Torvalds static void 248037515177SNeilBrown nfs4_set_claim_prev(struct nfsd4_open *open) 24811da177e4SLinus Torvalds { 24821da177e4SLinus Torvalds open->op_stateowner->so_confirmed = 1; 2483c7b9a459SNeilBrown open->op_stateowner->so_client->cl_firststate = 1; 24841da177e4SLinus Torvalds } 24851da177e4SLinus Torvalds 24861da177e4SLinus Torvalds /* 24871da177e4SLinus Torvalds * Attempt to hand out a delegation. 24881da177e4SLinus Torvalds */ 24891da177e4SLinus Torvalds static void 24901da177e4SLinus Torvalds nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_stateid *stp) 24911da177e4SLinus Torvalds { 24921da177e4SLinus Torvalds struct nfs4_delegation *dp; 24931da177e4SLinus Torvalds struct nfs4_stateowner *sop = stp->st_stateowner; 24942bf23875SJ. Bruce Fields int cb_up = atomic_read(&sop->so_client->cl_cb_set); 24951da177e4SLinus Torvalds struct file_lock fl, *flp = &fl; 24961da177e4SLinus Torvalds int status, flag = 0; 24971da177e4SLinus Torvalds 24981da177e4SLinus Torvalds flag = NFS4_OPEN_DELEGATE_NONE; 24997b190fecSNeilBrown open->op_recall = 0; 25007b190fecSNeilBrown switch (open->op_claim_type) { 25017b190fecSNeilBrown case NFS4_OPEN_CLAIM_PREVIOUS: 25022bf23875SJ. Bruce Fields if (!cb_up) 25037b190fecSNeilBrown open->op_recall = 1; 25047b190fecSNeilBrown flag = open->op_delegate_type; 25057b190fecSNeilBrown if (flag == NFS4_OPEN_DELEGATE_NONE) 25061da177e4SLinus Torvalds goto out; 25077b190fecSNeilBrown break; 25087b190fecSNeilBrown case NFS4_OPEN_CLAIM_NULL: 25097b190fecSNeilBrown /* Let's not give out any delegations till everyone's 25107b190fecSNeilBrown * had the chance to reclaim theirs.... */ 2511af558e33SJ. Bruce Fields if (locks_in_grace()) 25127b190fecSNeilBrown goto out; 25132bf23875SJ. Bruce Fields if (!cb_up || !sop->so_confirmed) 25147b190fecSNeilBrown goto out; 25151da177e4SLinus Torvalds if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) 25161da177e4SLinus Torvalds flag = NFS4_OPEN_DELEGATE_WRITE; 25171da177e4SLinus Torvalds else 25181da177e4SLinus Torvalds flag = NFS4_OPEN_DELEGATE_READ; 25197b190fecSNeilBrown break; 25207b190fecSNeilBrown default: 25217b190fecSNeilBrown goto out; 25227b190fecSNeilBrown } 25231da177e4SLinus Torvalds 25241da177e4SLinus Torvalds dp = alloc_init_deleg(sop->so_client, stp, fh, flag); 25251da177e4SLinus Torvalds if (dp == NULL) { 25261da177e4SLinus Torvalds flag = NFS4_OPEN_DELEGATE_NONE; 25271da177e4SLinus Torvalds goto out; 25281da177e4SLinus Torvalds } 25291da177e4SLinus Torvalds locks_init_lock(&fl); 25301da177e4SLinus Torvalds fl.fl_lmops = &nfsd_lease_mng_ops; 25311da177e4SLinus Torvalds fl.fl_flags = FL_LEASE; 25329167f501SFelix Blyakher fl.fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK; 25331da177e4SLinus Torvalds fl.fl_end = OFFSET_MAX; 25341da177e4SLinus Torvalds fl.fl_owner = (fl_owner_t)dp; 2535f9d7562fSJ. Bruce Fields fl.fl_file = find_readable_file(stp->st_file); 2536f9d7562fSJ. Bruce Fields BUG_ON(!fl.fl_file); 25371da177e4SLinus Torvalds fl.fl_pid = current->tgid; 25381da177e4SLinus Torvalds 2539a9933ceaSJ. Bruce Fields /* vfs_setlease checks to see if delegation should be handed out. 25401da177e4SLinus Torvalds * the lock_manager callbacks fl_mylease and fl_change are used 25411da177e4SLinus Torvalds */ 2542f9d7562fSJ. Bruce Fields if ((status = vfs_setlease(fl.fl_file, fl.fl_type, &flp))) { 25431da177e4SLinus Torvalds dprintk("NFSD: setlease failed [%d], no delegation\n", status); 2544c907132dSNeilBrown unhash_delegation(dp); 25451da177e4SLinus Torvalds flag = NFS4_OPEN_DELEGATE_NONE; 25461da177e4SLinus Torvalds goto out; 25471da177e4SLinus Torvalds } 25481da177e4SLinus Torvalds 25491da177e4SLinus Torvalds memcpy(&open->op_delegate_stateid, &dp->dl_stateid, sizeof(dp->dl_stateid)); 25501da177e4SLinus Torvalds 25518c10cbdbSBenny Halevy dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", 25528c10cbdbSBenny Halevy STATEID_VAL(&dp->dl_stateid)); 25531da177e4SLinus Torvalds out: 25547b190fecSNeilBrown if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS 25557b190fecSNeilBrown && flag == NFS4_OPEN_DELEGATE_NONE 25567b190fecSNeilBrown && open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) 25572fdada03SJ. Bruce Fields dprintk("NFSD: WARNING: refusing delegation reclaim\n"); 25581da177e4SLinus Torvalds open->op_delegate_type = flag; 25591da177e4SLinus Torvalds } 25601da177e4SLinus Torvalds 25611da177e4SLinus Torvalds /* 25621da177e4SLinus Torvalds * called with nfs4_lock_state() held. 25631da177e4SLinus Torvalds */ 2564b37ad28bSAl Viro __be32 25651da177e4SLinus Torvalds nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) 25661da177e4SLinus Torvalds { 25676668958fSAndy Adamson struct nfsd4_compoundres *resp = rqstp->rq_resp; 25681da177e4SLinus Torvalds struct nfs4_file *fp = NULL; 25691da177e4SLinus Torvalds struct inode *ino = current_fh->fh_dentry->d_inode; 25701da177e4SLinus Torvalds struct nfs4_stateid *stp = NULL; 2571567d9829SNeilBrown struct nfs4_delegation *dp = NULL; 2572b37ad28bSAl Viro __be32 status; 25731da177e4SLinus Torvalds 25741da177e4SLinus Torvalds status = nfserr_inval; 2575d87a8adeSAndy Adamson if (!access_valid(open->op_share_access, resp->cstate.minorversion) 2576ba5a6a19SJ. Bruce Fields || !deny_valid(open->op_share_deny)) 25771da177e4SLinus Torvalds goto out; 25781da177e4SLinus Torvalds /* 25791da177e4SLinus Torvalds * Lookup file; if found, lookup stateid and check open request, 25801da177e4SLinus Torvalds * and check for delegations in the process of being recalled. 25811da177e4SLinus Torvalds * If not found, create the nfs4_file struct 25821da177e4SLinus Torvalds */ 25831da177e4SLinus Torvalds fp = find_file(ino); 25841da177e4SLinus Torvalds if (fp) { 25851da177e4SLinus Torvalds if ((status = nfs4_check_open(fp, open, &stp))) 25861da177e4SLinus Torvalds goto out; 2587c44c5eebSNeilBrown status = nfs4_check_deleg(fp, open, &dp); 2588c44c5eebSNeilBrown if (status) 2589c44c5eebSNeilBrown goto out; 25901da177e4SLinus Torvalds } else { 2591c44c5eebSNeilBrown status = nfserr_bad_stateid; 2592c44c5eebSNeilBrown if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) 2593c44c5eebSNeilBrown goto out; 25941da177e4SLinus Torvalds status = nfserr_resource; 25951da177e4SLinus Torvalds fp = alloc_init_file(ino); 25961da177e4SLinus Torvalds if (fp == NULL) 25971da177e4SLinus Torvalds goto out; 25981da177e4SLinus Torvalds } 25991da177e4SLinus Torvalds 26001da177e4SLinus Torvalds /* 26011da177e4SLinus Torvalds * OPEN the file, or upgrade an existing OPEN. 26021da177e4SLinus Torvalds * If truncate fails, the OPEN fails. 26031da177e4SLinus Torvalds */ 26041da177e4SLinus Torvalds if (stp) { 26051da177e4SLinus Torvalds /* Stateid was found, this is an OPEN upgrade */ 2606f9d7562fSJ. Bruce Fields status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); 26071da177e4SLinus Torvalds if (status) 26081da177e4SLinus Torvalds goto out; 2609444c2c07SNeilBrown update_stateid(&stp->st_stateid); 26101da177e4SLinus Torvalds } else { 2611f9d7562fSJ. Bruce Fields status = nfs4_new_open(rqstp, &stp, fp, current_fh, open); 2612567d9829SNeilBrown if (status) 26131da177e4SLinus Torvalds goto out; 26141da177e4SLinus Torvalds init_stateid(stp, fp, open); 26151da177e4SLinus Torvalds status = nfsd4_truncate(rqstp, current_fh, open); 26161da177e4SLinus Torvalds if (status) { 26172283963fSJ. Bruce Fields release_open_stateid(stp); 26181da177e4SLinus Torvalds goto out; 26191da177e4SLinus Torvalds } 26206668958fSAndy Adamson if (nfsd4_has_session(&resp->cstate)) 26216668958fSAndy Adamson update_stateid(&stp->st_stateid); 26221da177e4SLinus Torvalds } 26231da177e4SLinus Torvalds memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); 26241da177e4SLinus Torvalds 26254dc6ec00SJ. Bruce Fields if (nfsd4_has_session(&resp->cstate)) 26266668958fSAndy Adamson open->op_stateowner->so_confirmed = 1; 26276668958fSAndy Adamson 26281da177e4SLinus Torvalds /* 26291da177e4SLinus Torvalds * Attempt to hand out a delegation. No error return, because the 26301da177e4SLinus Torvalds * OPEN succeeds even if we fail. 26311da177e4SLinus Torvalds */ 26321da177e4SLinus Torvalds nfs4_open_delegation(current_fh, open, stp); 26331da177e4SLinus Torvalds 26341da177e4SLinus Torvalds status = nfs_ok; 26351da177e4SLinus Torvalds 26368c10cbdbSBenny Halevy dprintk("%s: stateid=" STATEID_FMT "\n", __func__, 26378c10cbdbSBenny Halevy STATEID_VAL(&stp->st_stateid)); 26381da177e4SLinus Torvalds out: 263913cd2184SNeilBrown if (fp) 264013cd2184SNeilBrown put_nfs4_file(fp); 264137515177SNeilBrown if (status == 0 && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) 264237515177SNeilBrown nfs4_set_claim_prev(open); 26431da177e4SLinus Torvalds /* 26441da177e4SLinus Torvalds * To finish the open response, we just need to set the rflags. 26451da177e4SLinus Torvalds */ 26461da177e4SLinus Torvalds open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX; 26476668958fSAndy Adamson if (!open->op_stateowner->so_confirmed && 26486668958fSAndy Adamson !nfsd4_has_session(&resp->cstate)) 26491da177e4SLinus Torvalds open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM; 26501da177e4SLinus Torvalds 26511da177e4SLinus Torvalds return status; 26521da177e4SLinus Torvalds } 26531da177e4SLinus Torvalds 2654b37ad28bSAl Viro __be32 2655b591480bSJ.Bruce Fields nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 2656b591480bSJ.Bruce Fields clientid_t *clid) 26571da177e4SLinus Torvalds { 26581da177e4SLinus Torvalds struct nfs4_client *clp; 2659b37ad28bSAl Viro __be32 status; 26601da177e4SLinus Torvalds 26611da177e4SLinus Torvalds nfs4_lock_state(); 26621da177e4SLinus Torvalds dprintk("process_renew(%08x/%08x): starting\n", 26631da177e4SLinus Torvalds clid->cl_boot, clid->cl_id); 26641da177e4SLinus Torvalds status = nfserr_stale_clientid; 26651da177e4SLinus Torvalds if (STALE_CLIENTID(clid)) 26661da177e4SLinus Torvalds goto out; 26671da177e4SLinus Torvalds clp = find_confirmed_client(clid); 26681da177e4SLinus Torvalds status = nfserr_expired; 26691da177e4SLinus Torvalds if (clp == NULL) { 26701da177e4SLinus Torvalds /* We assume the client took too long to RENEW. */ 26711da177e4SLinus Torvalds dprintk("nfsd4_renew: clientid not found!\n"); 26721da177e4SLinus Torvalds goto out; 26731da177e4SLinus Torvalds } 26741da177e4SLinus Torvalds renew_client(clp); 26751da177e4SLinus Torvalds status = nfserr_cb_path_down; 2676ea1da636SNeilBrown if (!list_empty(&clp->cl_delegations) 26772bf23875SJ. Bruce Fields && !atomic_read(&clp->cl_cb_set)) 26781da177e4SLinus Torvalds goto out; 26791da177e4SLinus Torvalds status = nfs_ok; 26801da177e4SLinus Torvalds out: 26811da177e4SLinus Torvalds nfs4_unlock_state(); 26821da177e4SLinus Torvalds return status; 26831da177e4SLinus Torvalds } 26841da177e4SLinus Torvalds 2685af558e33SJ. Bruce Fields struct lock_manager nfsd4_manager = { 2686af558e33SJ. Bruce Fields }; 2687af558e33SJ. Bruce Fields 2688a76b4319SNeilBrown static void 2689af558e33SJ. Bruce Fields nfsd4_end_grace(void) 2690a76b4319SNeilBrown { 2691a76b4319SNeilBrown dprintk("NFSD: end of grace period\n"); 2692c7b9a459SNeilBrown nfsd4_recdir_purge_old(); 2693af558e33SJ. Bruce Fields locks_end_grace(&nfsd4_manager); 2694e46b498cSJ. Bruce Fields /* 2695e46b498cSJ. Bruce Fields * Now that every NFSv4 client has had the chance to recover and 2696e46b498cSJ. Bruce Fields * to see the (possibly new, possibly shorter) lease time, we 2697e46b498cSJ. Bruce Fields * can safely set the next grace time to the current lease time: 2698e46b498cSJ. Bruce Fields */ 2699e46b498cSJ. Bruce Fields nfsd4_grace = nfsd4_lease; 2700a76b4319SNeilBrown } 2701a76b4319SNeilBrown 2702fd39ca9aSNeilBrown static time_t 27031da177e4SLinus Torvalds nfs4_laundromat(void) 27041da177e4SLinus Torvalds { 27051da177e4SLinus Torvalds struct nfs4_client *clp; 27061da177e4SLinus Torvalds struct nfs4_stateowner *sop; 27071da177e4SLinus Torvalds struct nfs4_delegation *dp; 27081da177e4SLinus Torvalds struct list_head *pos, *next, reaplist; 2709cf07d2eaSJ. Bruce Fields time_t cutoff = get_seconds() - nfsd4_lease; 2710cf07d2eaSJ. Bruce Fields time_t t, clientid_val = nfsd4_lease; 2711cf07d2eaSJ. Bruce Fields time_t u, test_val = nfsd4_lease; 27121da177e4SLinus Torvalds 27131da177e4SLinus Torvalds nfs4_lock_state(); 27141da177e4SLinus Torvalds 27151da177e4SLinus Torvalds dprintk("NFSD: laundromat service - starting\n"); 2716af558e33SJ. Bruce Fields if (locks_in_grace()) 2717af558e33SJ. Bruce Fields nfsd4_end_grace(); 271836acb66bSBenny Halevy INIT_LIST_HEAD(&reaplist); 271936acb66bSBenny Halevy spin_lock(&client_lock); 27201da177e4SLinus Torvalds list_for_each_safe(pos, next, &client_lru) { 27211da177e4SLinus Torvalds clp = list_entry(pos, struct nfs4_client, cl_lru); 27221da177e4SLinus Torvalds if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { 27231da177e4SLinus Torvalds t = clp->cl_time - cutoff; 27241da177e4SLinus Torvalds if (clientid_val > t) 27251da177e4SLinus Torvalds clientid_val = t; 27261da177e4SLinus Torvalds break; 27271da177e4SLinus Torvalds } 2728d7682988SBenny Halevy if (atomic_read(&clp->cl_refcount)) { 2729d7682988SBenny Halevy dprintk("NFSD: client in use (clientid %08x)\n", 2730d7682988SBenny Halevy clp->cl_clientid.cl_id); 2731d7682988SBenny Halevy continue; 2732d7682988SBenny Halevy } 2733d7682988SBenny Halevy unhash_client_locked(clp); 2734d7682988SBenny Halevy list_add(&clp->cl_lru, &reaplist); 273536acb66bSBenny Halevy } 273636acb66bSBenny Halevy spin_unlock(&client_lock); 273736acb66bSBenny Halevy list_for_each_safe(pos, next, &reaplist) { 273836acb66bSBenny Halevy clp = list_entry(pos, struct nfs4_client, cl_lru); 27391da177e4SLinus Torvalds dprintk("NFSD: purging unused client (clientid %08x)\n", 27401da177e4SLinus Torvalds clp->cl_clientid.cl_id); 2741c7b9a459SNeilBrown nfsd4_remove_clid_dir(clp); 27421da177e4SLinus Torvalds expire_client(clp); 27431da177e4SLinus Torvalds } 27441da177e4SLinus Torvalds spin_lock(&recall_lock); 27451da177e4SLinus Torvalds list_for_each_safe(pos, next, &del_recall_lru) { 27461da177e4SLinus Torvalds dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); 27471da177e4SLinus Torvalds if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { 27481da177e4SLinus Torvalds u = dp->dl_time - cutoff; 27491da177e4SLinus Torvalds if (test_val > u) 27501da177e4SLinus Torvalds test_val = u; 27511da177e4SLinus Torvalds break; 27521da177e4SLinus Torvalds } 27531da177e4SLinus Torvalds dprintk("NFSD: purging unused delegation dp %p, fp %p\n", 27541da177e4SLinus Torvalds dp, dp->dl_flock); 27551da177e4SLinus Torvalds list_move(&dp->dl_recall_lru, &reaplist); 27561da177e4SLinus Torvalds } 27571da177e4SLinus Torvalds spin_unlock(&recall_lock); 27581da177e4SLinus Torvalds list_for_each_safe(pos, next, &reaplist) { 27591da177e4SLinus Torvalds dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); 27601da177e4SLinus Torvalds list_del_init(&dp->dl_recall_lru); 27611da177e4SLinus Torvalds unhash_delegation(dp); 27621da177e4SLinus Torvalds } 2763cf07d2eaSJ. Bruce Fields test_val = nfsd4_lease; 27641da177e4SLinus Torvalds list_for_each_safe(pos, next, &close_lru) { 27651da177e4SLinus Torvalds sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); 27661da177e4SLinus Torvalds if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { 27671da177e4SLinus Torvalds u = sop->so_time - cutoff; 27681da177e4SLinus Torvalds if (test_val > u) 27691da177e4SLinus Torvalds test_val = u; 27701da177e4SLinus Torvalds break; 27711da177e4SLinus Torvalds } 27721da177e4SLinus Torvalds dprintk("NFSD: purging unused open stateowner (so_id %d)\n", 27731da177e4SLinus Torvalds sop->so_id); 2774f044ff83SJ. Bruce Fields release_openowner(sop); 27751da177e4SLinus Torvalds } 27761da177e4SLinus Torvalds if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) 27771da177e4SLinus Torvalds clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT; 27781da177e4SLinus Torvalds nfs4_unlock_state(); 27791da177e4SLinus Torvalds return clientid_val; 27801da177e4SLinus Torvalds } 27811da177e4SLinus Torvalds 2782a254b246SHarvey Harrison static struct workqueue_struct *laundry_wq; 2783a254b246SHarvey Harrison static void laundromat_main(struct work_struct *); 2784a254b246SHarvey Harrison static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main); 2785a254b246SHarvey Harrison 2786a254b246SHarvey Harrison static void 2787c4028958SDavid Howells laundromat_main(struct work_struct *not_used) 27881da177e4SLinus Torvalds { 27891da177e4SLinus Torvalds time_t t; 27901da177e4SLinus Torvalds 27911da177e4SLinus Torvalds t = nfs4_laundromat(); 27921da177e4SLinus Torvalds dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); 279358da282bSNeilBrown queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); 27941da177e4SLinus Torvalds } 27951da177e4SLinus Torvalds 2796fd39ca9aSNeilBrown static struct nfs4_stateowner * 2797f8816512SNeilBrown search_close_lru(u32 st_id, int flags) 2798f8816512SNeilBrown { 27991da177e4SLinus Torvalds struct nfs4_stateowner *local = NULL; 28001da177e4SLinus Torvalds 28011da177e4SLinus Torvalds if (flags & CLOSE_STATE) { 28021da177e4SLinus Torvalds list_for_each_entry(local, &close_lru, so_close_lru) { 28031da177e4SLinus Torvalds if (local->so_id == st_id) 28041da177e4SLinus Torvalds return local; 28051da177e4SLinus Torvalds } 28061da177e4SLinus Torvalds } 28071da177e4SLinus Torvalds return NULL; 28081da177e4SLinus Torvalds } 28091da177e4SLinus Torvalds 28101da177e4SLinus Torvalds static inline int 28111da177e4SLinus Torvalds nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp) 28121da177e4SLinus Torvalds { 2813f9d7562fSJ. Bruce Fields return fhp->fh_dentry->d_inode != stp->st_file->fi_inode; 28141da177e4SLinus Torvalds } 28151da177e4SLinus Torvalds 28161da177e4SLinus Torvalds static int 28171da177e4SLinus Torvalds STALE_STATEID(stateid_t *stateid) 28181da177e4SLinus Torvalds { 2819e4e83ea4SJ. Bruce Fields if (stateid->si_boot == boot_time) 2820e4e83ea4SJ. Bruce Fields return 0; 28218c10cbdbSBenny Halevy dprintk("NFSD: stale stateid " STATEID_FMT "!\n", 28228c10cbdbSBenny Halevy STATEID_VAL(stateid)); 28231da177e4SLinus Torvalds return 1; 28241da177e4SLinus Torvalds } 28251da177e4SLinus Torvalds 28261da177e4SLinus Torvalds static inline int 28271da177e4SLinus Torvalds access_permit_read(unsigned long access_bmap) 28281da177e4SLinus Torvalds { 28291da177e4SLinus Torvalds return test_bit(NFS4_SHARE_ACCESS_READ, &access_bmap) || 28301da177e4SLinus Torvalds test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap) || 28311da177e4SLinus Torvalds test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap); 28321da177e4SLinus Torvalds } 28331da177e4SLinus Torvalds 28341da177e4SLinus Torvalds static inline int 28351da177e4SLinus Torvalds access_permit_write(unsigned long access_bmap) 28361da177e4SLinus Torvalds { 28371da177e4SLinus Torvalds return test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap) || 28381da177e4SLinus Torvalds test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap); 28391da177e4SLinus Torvalds } 28401da177e4SLinus Torvalds 28411da177e4SLinus Torvalds static 2842b37ad28bSAl Viro __be32 nfs4_check_openmode(struct nfs4_stateid *stp, int flags) 28431da177e4SLinus Torvalds { 2844b37ad28bSAl Viro __be32 status = nfserr_openmode; 28451da177e4SLinus Torvalds 284602921914SJ. Bruce Fields /* For lock stateid's, we test the parent open, not the lock: */ 284702921914SJ. Bruce Fields if (stp->st_openstp) 284802921914SJ. Bruce Fields stp = stp->st_openstp; 28491da177e4SLinus Torvalds if ((flags & WR_STATE) && (!access_permit_write(stp->st_access_bmap))) 28501da177e4SLinus Torvalds goto out; 28511da177e4SLinus Torvalds if ((flags & RD_STATE) && (!access_permit_read(stp->st_access_bmap))) 28521da177e4SLinus Torvalds goto out; 28531da177e4SLinus Torvalds status = nfs_ok; 28541da177e4SLinus Torvalds out: 28551da177e4SLinus Torvalds return status; 28561da177e4SLinus Torvalds } 28571da177e4SLinus Torvalds 2858b37ad28bSAl Viro static inline __be32 28591da177e4SLinus Torvalds check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) 28601da177e4SLinus Torvalds { 2861203a8c8eSJ. Bruce Fields if (ONE_STATEID(stateid) && (flags & RD_STATE)) 28621da177e4SLinus Torvalds return nfs_ok; 2863af558e33SJ. Bruce Fields else if (locks_in_grace()) { 28641da177e4SLinus Torvalds /* Answer in remaining cases depends on existance of 28651da177e4SLinus Torvalds * conflicting state; so we must wait out the grace period. */ 28661da177e4SLinus Torvalds return nfserr_grace; 28671da177e4SLinus Torvalds } else if (flags & WR_STATE) 28681da177e4SLinus Torvalds return nfs4_share_conflict(current_fh, 28691da177e4SLinus Torvalds NFS4_SHARE_DENY_WRITE); 28701da177e4SLinus Torvalds else /* (flags & RD_STATE) && ZERO_STATEID(stateid) */ 28711da177e4SLinus Torvalds return nfs4_share_conflict(current_fh, 28721da177e4SLinus Torvalds NFS4_SHARE_DENY_READ); 28731da177e4SLinus Torvalds } 28741da177e4SLinus Torvalds 28751da177e4SLinus Torvalds /* 28761da177e4SLinus Torvalds * Allow READ/WRITE during grace period on recovered state only for files 28771da177e4SLinus Torvalds * that are not able to provide mandatory locking. 28781da177e4SLinus Torvalds */ 28791da177e4SLinus Torvalds static inline int 288018f82731SJ. Bruce Fields grace_disallows_io(struct inode *inode) 28811da177e4SLinus Torvalds { 2882203a8c8eSJ. Bruce Fields return locks_in_grace() && mandatory_lock(inode); 28831da177e4SLinus Torvalds } 28841da177e4SLinus Torvalds 28856668958fSAndy Adamson static int check_stateid_generation(stateid_t *in, stateid_t *ref, int flags) 28860836f587SJ. Bruce Fields { 28876668958fSAndy Adamson /* 28886668958fSAndy Adamson * When sessions are used the stateid generation number is ignored 28896668958fSAndy Adamson * when it is zero. 28906668958fSAndy Adamson */ 28916668958fSAndy Adamson if ((flags & HAS_SESSION) && in->si_generation == 0) 28926668958fSAndy Adamson goto out; 28936668958fSAndy Adamson 28940836f587SJ. Bruce Fields /* If the client sends us a stateid from the future, it's buggy: */ 28950836f587SJ. Bruce Fields if (in->si_generation > ref->si_generation) 28960836f587SJ. Bruce Fields return nfserr_bad_stateid; 28970836f587SJ. Bruce Fields /* 28980836f587SJ. Bruce Fields * The following, however, can happen. For example, if the 28990836f587SJ. Bruce Fields * client sends an open and some IO at the same time, the open 29000836f587SJ. Bruce Fields * may bump si_generation while the IO is still in flight. 29010836f587SJ. Bruce Fields * Thanks to hard links and renames, the client never knows what 29020836f587SJ. Bruce Fields * file an open will affect. So it could avoid that situation 29030836f587SJ. Bruce Fields * only by serializing all opens and IO from the same open 29040836f587SJ. Bruce Fields * owner. To recover from the old_stateid error, the client 29050836f587SJ. Bruce Fields * will just have to retry the IO: 29060836f587SJ. Bruce Fields */ 29070836f587SJ. Bruce Fields if (in->si_generation < ref->si_generation) 29080836f587SJ. Bruce Fields return nfserr_old_stateid; 29096668958fSAndy Adamson out: 29100836f587SJ. Bruce Fields return nfs_ok; 29110836f587SJ. Bruce Fields } 29120836f587SJ. Bruce Fields 29133e633079SJ. Bruce Fields static int is_delegation_stateid(stateid_t *stateid) 29143e633079SJ. Bruce Fields { 29153e633079SJ. Bruce Fields return stateid->si_fileid == 0; 29163e633079SJ. Bruce Fields } 29173e633079SJ. Bruce Fields 29181da177e4SLinus Torvalds /* 29191da177e4SLinus Torvalds * Checks for stateid operations 29201da177e4SLinus Torvalds */ 2921b37ad28bSAl Viro __be32 2922dd453dfdSBenny Halevy nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, 2923dd453dfdSBenny Halevy stateid_t *stateid, int flags, struct file **filpp) 29241da177e4SLinus Torvalds { 29251da177e4SLinus Torvalds struct nfs4_stateid *stp = NULL; 29261da177e4SLinus Torvalds struct nfs4_delegation *dp = NULL; 2927dd453dfdSBenny Halevy struct svc_fh *current_fh = &cstate->current_fh; 29281da177e4SLinus Torvalds struct inode *ino = current_fh->fh_dentry->d_inode; 2929b37ad28bSAl Viro __be32 status; 29301da177e4SLinus Torvalds 29311da177e4SLinus Torvalds if (filpp) 29321da177e4SLinus Torvalds *filpp = NULL; 29331da177e4SLinus Torvalds 293418f82731SJ. Bruce Fields if (grace_disallows_io(ino)) 29351da177e4SLinus Torvalds return nfserr_grace; 29361da177e4SLinus Torvalds 29376668958fSAndy Adamson if (nfsd4_has_session(cstate)) 29386668958fSAndy Adamson flags |= HAS_SESSION; 29396668958fSAndy Adamson 29401da177e4SLinus Torvalds if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) 29411da177e4SLinus Torvalds return check_special_stateids(current_fh, stateid, flags); 29421da177e4SLinus Torvalds 29431da177e4SLinus Torvalds status = nfserr_stale_stateid; 29441da177e4SLinus Torvalds if (STALE_STATEID(stateid)) 29451da177e4SLinus Torvalds goto out; 29461da177e4SLinus Torvalds 29471da177e4SLinus Torvalds status = nfserr_bad_stateid; 29483e633079SJ. Bruce Fields if (is_delegation_stateid(stateid)) { 2949a4455be0SJ. Bruce Fields dp = find_delegation_stateid(ino, stateid); 2950e4e83ea4SJ. Bruce Fields if (!dp) 29511da177e4SLinus Torvalds goto out; 29526668958fSAndy Adamson status = check_stateid_generation(stateid, &dp->dl_stateid, 29536668958fSAndy Adamson flags); 29540c2a498fSJ. Bruce Fields if (status) 29550c2a498fSJ. Bruce Fields goto out; 2956dc9bf700SJ. Bruce Fields status = nfs4_check_delegmode(dp, flags); 2957dc9bf700SJ. Bruce Fields if (status) 2958dc9bf700SJ. Bruce Fields goto out; 2959dc9bf700SJ. Bruce Fields renew_client(dp->dl_client); 2960dc9bf700SJ. Bruce Fields if (filpp) 2961f9d7562fSJ. Bruce Fields *filpp = find_readable_file(dp->dl_file); 2962f9d7562fSJ. Bruce Fields BUG_ON(!*filpp); 29631da177e4SLinus Torvalds } else { /* open or lock stateid */ 2964a4455be0SJ. Bruce Fields stp = find_stateid(stateid, flags); 2965e4e83ea4SJ. Bruce Fields if (!stp) 29661da177e4SLinus Torvalds goto out; 29676150ef0dSJ. Bruce Fields if (nfs4_check_fh(current_fh, stp)) 29681da177e4SLinus Torvalds goto out; 29691da177e4SLinus Torvalds if (!stp->st_stateowner->so_confirmed) 29701da177e4SLinus Torvalds goto out; 29716668958fSAndy Adamson status = check_stateid_generation(stateid, &stp->st_stateid, 29726668958fSAndy Adamson flags); 29730836f587SJ. Bruce Fields if (status) 29741da177e4SLinus Torvalds goto out; 2975a4455be0SJ. Bruce Fields status = nfs4_check_openmode(stp, flags); 2976a4455be0SJ. Bruce Fields if (status) 29771da177e4SLinus Torvalds goto out; 29781da177e4SLinus Torvalds renew_client(stp->st_stateowner->so_client); 2979f9d7562fSJ. Bruce Fields if (filpp) { 2980f9d7562fSJ. Bruce Fields if (flags & RD_STATE) 2981f9d7562fSJ. Bruce Fields *filpp = find_readable_file(stp->st_file); 2982f9d7562fSJ. Bruce Fields else 2983f9d7562fSJ. Bruce Fields *filpp = find_writeable_file(stp->st_file); 2984f9d7562fSJ. Bruce Fields } 29851da177e4SLinus Torvalds } 29861da177e4SLinus Torvalds status = nfs_ok; 29871da177e4SLinus Torvalds out: 29881da177e4SLinus Torvalds return status; 29891da177e4SLinus Torvalds } 29901da177e4SLinus Torvalds 29914c4cd222SNeilBrown static inline int 29924c4cd222SNeilBrown setlkflg (int type) 29934c4cd222SNeilBrown { 29944c4cd222SNeilBrown return (type == NFS4_READW_LT || type == NFS4_READ_LT) ? 29954c4cd222SNeilBrown RD_STATE : WR_STATE; 29964c4cd222SNeilBrown } 29971da177e4SLinus Torvalds 29981da177e4SLinus Torvalds /* 29991da177e4SLinus Torvalds * Checks for sequence id mutating operations. 30001da177e4SLinus Torvalds */ 3001b37ad28bSAl Viro static __be32 3002dd453dfdSBenny Halevy nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, 3003dd453dfdSBenny Halevy stateid_t *stateid, int flags, 3004dd453dfdSBenny Halevy struct nfs4_stateowner **sopp, 3005dd453dfdSBenny Halevy struct nfs4_stateid **stpp, struct nfsd4_lock *lock) 30061da177e4SLinus Torvalds { 30071da177e4SLinus Torvalds struct nfs4_stateid *stp; 30081da177e4SLinus Torvalds struct nfs4_stateowner *sop; 3009dd453dfdSBenny Halevy struct svc_fh *current_fh = &cstate->current_fh; 30100836f587SJ. Bruce Fields __be32 status; 30111da177e4SLinus Torvalds 30128c10cbdbSBenny Halevy dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__, 30138c10cbdbSBenny Halevy seqid, STATEID_VAL(stateid)); 30141da177e4SLinus Torvalds 30151da177e4SLinus Torvalds *stpp = NULL; 30161da177e4SLinus Torvalds *sopp = NULL; 30171da177e4SLinus Torvalds 30181da177e4SLinus Torvalds if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) { 30192fdada03SJ. Bruce Fields dprintk("NFSD: preprocess_seqid_op: magic stateid!\n"); 30203a4f98bbSNeilBrown return nfserr_bad_stateid; 30211da177e4SLinus Torvalds } 30221da177e4SLinus Torvalds 30231da177e4SLinus Torvalds if (STALE_STATEID(stateid)) 30243a4f98bbSNeilBrown return nfserr_stale_stateid; 30256668958fSAndy Adamson 30266668958fSAndy Adamson if (nfsd4_has_session(cstate)) 30276668958fSAndy Adamson flags |= HAS_SESSION; 30286668958fSAndy Adamson 30291da177e4SLinus Torvalds /* 30301da177e4SLinus Torvalds * We return BAD_STATEID if filehandle doesn't match stateid, 30311da177e4SLinus Torvalds * the confirmed flag is incorrecly set, or the generation 30321da177e4SLinus Torvalds * number is incorrect. 30331da177e4SLinus Torvalds */ 3034f8816512SNeilBrown stp = find_stateid(stateid, flags); 3035f8816512SNeilBrown if (stp == NULL) { 3036f8816512SNeilBrown /* 3037f8816512SNeilBrown * Also, we should make sure this isn't just the result of 3038f8816512SNeilBrown * a replayed close: 3039f8816512SNeilBrown */ 3040f8816512SNeilBrown sop = search_close_lru(stateid->si_stateownerid, flags); 3041f8816512SNeilBrown if (sop == NULL) 3042e4e83ea4SJ. Bruce Fields return nfserr_bad_stateid; 3043f8816512SNeilBrown *sopp = sop; 3044f8816512SNeilBrown goto check_replay; 3045f8816512SNeilBrown } 30461da177e4SLinus Torvalds 304739325bd0SJ. Bruce Fields *stpp = stp; 304839325bd0SJ. Bruce Fields *sopp = sop = stp->st_stateowner; 304939325bd0SJ. Bruce Fields 30504c4cd222SNeilBrown if (lock) { 30514c4cd222SNeilBrown clientid_t *lockclid = &lock->v.new.clientid; 30521da177e4SLinus Torvalds struct nfs4_client *clp = sop->so_client; 30534c4cd222SNeilBrown int lkflg = 0; 3054b37ad28bSAl Viro __be32 status; 30551da177e4SLinus Torvalds 30564c4cd222SNeilBrown lkflg = setlkflg(lock->lk_type); 30574c4cd222SNeilBrown 30584c4cd222SNeilBrown if (lock->lk_is_new) { 30591da177e4SLinus Torvalds if (!sop->so_is_open_owner) 30603a4f98bbSNeilBrown return nfserr_bad_stateid; 306160adfc50SAndy Adamson if (!(flags & HAS_SESSION) && 306260adfc50SAndy Adamson !same_clid(&clp->cl_clientid, lockclid)) 30633a4f98bbSNeilBrown return nfserr_bad_stateid; 30644c4cd222SNeilBrown /* stp is the open stateid */ 30654c4cd222SNeilBrown status = nfs4_check_openmode(stp, lkflg); 30664c4cd222SNeilBrown if (status) 30674c4cd222SNeilBrown return status; 30684c4cd222SNeilBrown } else { 30694c4cd222SNeilBrown /* stp is the lock stateid */ 30704c4cd222SNeilBrown status = nfs4_check_openmode(stp->st_openstp, lkflg); 30714c4cd222SNeilBrown if (status) 30724c4cd222SNeilBrown return status; 30734c4cd222SNeilBrown } 30741da177e4SLinus Torvalds } 30751da177e4SLinus Torvalds 3076f3362737SJ. Bruce Fields if (nfs4_check_fh(current_fh, stp)) { 30772fdada03SJ. Bruce Fields dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n"); 30783a4f98bbSNeilBrown return nfserr_bad_stateid; 30791da177e4SLinus Torvalds } 30801da177e4SLinus Torvalds 30811da177e4SLinus Torvalds /* 30821da177e4SLinus Torvalds * We now validate the seqid and stateid generation numbers. 30831da177e4SLinus Torvalds * For the moment, we ignore the possibility of 30841da177e4SLinus Torvalds * generation number wraparound. 30851da177e4SLinus Torvalds */ 30866668958fSAndy Adamson if (!(flags & HAS_SESSION) && seqid != sop->so_seqid) 30871da177e4SLinus Torvalds goto check_replay; 30881da177e4SLinus Torvalds 30893a4f98bbSNeilBrown if (sop->so_confirmed && flags & CONFIRM) { 30902fdada03SJ. Bruce Fields dprintk("NFSD: preprocess_seqid_op: expected" 30913a4f98bbSNeilBrown " unconfirmed stateowner!\n"); 30923a4f98bbSNeilBrown return nfserr_bad_stateid; 30931da177e4SLinus Torvalds } 30943a4f98bbSNeilBrown if (!sop->so_confirmed && !(flags & CONFIRM)) { 30952fdada03SJ. Bruce Fields dprintk("NFSD: preprocess_seqid_op: stateowner not" 30963a4f98bbSNeilBrown " confirmed yet!\n"); 30973a4f98bbSNeilBrown return nfserr_bad_stateid; 30981da177e4SLinus Torvalds } 30996668958fSAndy Adamson status = check_stateid_generation(stateid, &stp->st_stateid, flags); 31000836f587SJ. Bruce Fields if (status) 31010836f587SJ. Bruce Fields return status; 310252fd004eSNeilBrown renew_client(sop->so_client); 31033a4f98bbSNeilBrown return nfs_ok; 31041da177e4SLinus Torvalds 31051da177e4SLinus Torvalds check_replay: 3106bd9aac52SNeilBrown if (seqid == sop->so_seqid - 1) { 3107849823c5SNeil Brown dprintk("NFSD: preprocess_seqid_op: retransmission?\n"); 31081da177e4SLinus Torvalds /* indicate replay to calling function */ 3109a90b061cSAl Viro return nfserr_replay_me; 31101da177e4SLinus Torvalds } 31112fdada03SJ. Bruce Fields dprintk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d)\n", 31123a4f98bbSNeilBrown sop->so_seqid, seqid); 31133a4f98bbSNeilBrown *sopp = NULL; 31143a4f98bbSNeilBrown return nfserr_bad_seqid; 31151da177e4SLinus Torvalds } 31161da177e4SLinus Torvalds 3117b37ad28bSAl Viro __be32 3118ca364317SJ.Bruce Fields nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 3119a4f1706aSJ.Bruce Fields struct nfsd4_open_confirm *oc) 31201da177e4SLinus Torvalds { 3121b37ad28bSAl Viro __be32 status; 31221da177e4SLinus Torvalds struct nfs4_stateowner *sop; 31231da177e4SLinus Torvalds struct nfs4_stateid *stp; 31241da177e4SLinus Torvalds 31251da177e4SLinus Torvalds dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", 3126ca364317SJ.Bruce Fields (int)cstate->current_fh.fh_dentry->d_name.len, 3127ca364317SJ.Bruce Fields cstate->current_fh.fh_dentry->d_name.name); 31281da177e4SLinus Torvalds 3129ca364317SJ.Bruce Fields status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0); 3130a8cddc5dSJ. Bruce Fields if (status) 3131a8cddc5dSJ. Bruce Fields return status; 31321da177e4SLinus Torvalds 31331da177e4SLinus Torvalds nfs4_lock_state(); 31341da177e4SLinus Torvalds 3135dd453dfdSBenny Halevy if ((status = nfs4_preprocess_seqid_op(cstate, 3136ca364317SJ.Bruce Fields oc->oc_seqid, &oc->oc_req_stateid, 3137f3362737SJ. Bruce Fields CONFIRM | OPEN_STATE, 31381da177e4SLinus Torvalds &oc->oc_stateowner, &stp, NULL))) 31391da177e4SLinus Torvalds goto out; 31401da177e4SLinus Torvalds 31411da177e4SLinus Torvalds sop = oc->oc_stateowner; 31421da177e4SLinus Torvalds sop->so_confirmed = 1; 31431da177e4SLinus Torvalds update_stateid(&stp->st_stateid); 31441da177e4SLinus Torvalds memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t)); 31458c10cbdbSBenny Halevy dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", 31468c10cbdbSBenny Halevy __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stateid)); 3147c7b9a459SNeilBrown 3148c7b9a459SNeilBrown nfsd4_create_clid_dir(sop->so_client); 31491da177e4SLinus Torvalds out: 3150f2327d9aSNeil Brown if (oc->oc_stateowner) { 31511da177e4SLinus Torvalds nfs4_get_stateowner(oc->oc_stateowner); 3152a4f1706aSJ.Bruce Fields cstate->replay_owner = oc->oc_stateowner; 3153f2327d9aSNeil Brown } 31541da177e4SLinus Torvalds nfs4_unlock_state(); 31551da177e4SLinus Torvalds return status; 31561da177e4SLinus Torvalds } 31571da177e4SLinus Torvalds 31581da177e4SLinus Torvalds 31591da177e4SLinus Torvalds /* 31601da177e4SLinus Torvalds * unset all bits in union bitmap (bmap) that 31611da177e4SLinus Torvalds * do not exist in share (from successful OPEN_DOWNGRADE) 31621da177e4SLinus Torvalds */ 31631da177e4SLinus Torvalds static void 31641da177e4SLinus Torvalds reset_union_bmap_access(unsigned long access, unsigned long *bmap) 31651da177e4SLinus Torvalds { 31661da177e4SLinus Torvalds int i; 31671da177e4SLinus Torvalds for (i = 1; i < 4; i++) { 31681da177e4SLinus Torvalds if ((i & access) != i) 31691da177e4SLinus Torvalds __clear_bit(i, bmap); 31701da177e4SLinus Torvalds } 31711da177e4SLinus Torvalds } 31721da177e4SLinus Torvalds 31731da177e4SLinus Torvalds static void 31741da177e4SLinus Torvalds reset_union_bmap_deny(unsigned long deny, unsigned long *bmap) 31751da177e4SLinus Torvalds { 31761da177e4SLinus Torvalds int i; 31771da177e4SLinus Torvalds for (i = 0; i < 4; i++) { 31781da177e4SLinus Torvalds if ((i & deny) != i) 31791da177e4SLinus Torvalds __clear_bit(i, bmap); 31801da177e4SLinus Torvalds } 31811da177e4SLinus Torvalds } 31821da177e4SLinus Torvalds 3183b37ad28bSAl Viro __be32 3184ca364317SJ.Bruce Fields nfsd4_open_downgrade(struct svc_rqst *rqstp, 3185ca364317SJ.Bruce Fields struct nfsd4_compound_state *cstate, 3186a4f1706aSJ.Bruce Fields struct nfsd4_open_downgrade *od) 31871da177e4SLinus Torvalds { 3188b37ad28bSAl Viro __be32 status; 31891da177e4SLinus Torvalds struct nfs4_stateid *stp; 31901da177e4SLinus Torvalds unsigned int share_access; 31911da177e4SLinus Torvalds 31921da177e4SLinus Torvalds dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", 3193ca364317SJ.Bruce Fields (int)cstate->current_fh.fh_dentry->d_name.len, 3194ca364317SJ.Bruce Fields cstate->current_fh.fh_dentry->d_name.name); 31951da177e4SLinus Torvalds 3196d87a8adeSAndy Adamson if (!access_valid(od->od_share_access, cstate->minorversion) 3197ba5a6a19SJ. Bruce Fields || !deny_valid(od->od_share_deny)) 31981da177e4SLinus Torvalds return nfserr_inval; 31991da177e4SLinus Torvalds 32001da177e4SLinus Torvalds nfs4_lock_state(); 3201dd453dfdSBenny Halevy if ((status = nfs4_preprocess_seqid_op(cstate, 3202ca364317SJ.Bruce Fields od->od_seqid, 32031da177e4SLinus Torvalds &od->od_stateid, 3204f3362737SJ. Bruce Fields OPEN_STATE, 32051da177e4SLinus Torvalds &od->od_stateowner, &stp, NULL))) 32061da177e4SLinus Torvalds goto out; 32071da177e4SLinus Torvalds 32081da177e4SLinus Torvalds status = nfserr_inval; 32091da177e4SLinus Torvalds if (!test_bit(od->od_share_access, &stp->st_access_bmap)) { 32101da177e4SLinus Torvalds dprintk("NFSD:access not a subset current bitmap: 0x%lx, input access=%08x\n", 32111da177e4SLinus Torvalds stp->st_access_bmap, od->od_share_access); 32121da177e4SLinus Torvalds goto out; 32131da177e4SLinus Torvalds } 32141da177e4SLinus Torvalds if (!test_bit(od->od_share_deny, &stp->st_deny_bmap)) { 32151da177e4SLinus Torvalds dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n", 32161da177e4SLinus Torvalds stp->st_deny_bmap, od->od_share_deny); 32171da177e4SLinus Torvalds goto out; 32181da177e4SLinus Torvalds } 32191da177e4SLinus Torvalds set_access(&share_access, stp->st_access_bmap); 3220f9d7562fSJ. Bruce Fields nfs4_file_downgrade(stp->st_file, share_access & ~od->od_share_access); 32211da177e4SLinus Torvalds 32221da177e4SLinus Torvalds reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap); 32231da177e4SLinus Torvalds reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap); 32241da177e4SLinus Torvalds 32251da177e4SLinus Torvalds update_stateid(&stp->st_stateid); 32261da177e4SLinus Torvalds memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t)); 32271da177e4SLinus Torvalds status = nfs_ok; 32281da177e4SLinus Torvalds out: 3229f2327d9aSNeil Brown if (od->od_stateowner) { 32301da177e4SLinus Torvalds nfs4_get_stateowner(od->od_stateowner); 3231a4f1706aSJ.Bruce Fields cstate->replay_owner = od->od_stateowner; 3232f2327d9aSNeil Brown } 32331da177e4SLinus Torvalds nfs4_unlock_state(); 32341da177e4SLinus Torvalds return status; 32351da177e4SLinus Torvalds } 32361da177e4SLinus Torvalds 32371da177e4SLinus Torvalds /* 32381da177e4SLinus Torvalds * nfs4_unlock_state() called after encode 32391da177e4SLinus Torvalds */ 3240b37ad28bSAl Viro __be32 3241ca364317SJ.Bruce Fields nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 3242a4f1706aSJ.Bruce Fields struct nfsd4_close *close) 32431da177e4SLinus Torvalds { 3244b37ad28bSAl Viro __be32 status; 32451da177e4SLinus Torvalds struct nfs4_stateid *stp; 32461da177e4SLinus Torvalds 32471da177e4SLinus Torvalds dprintk("NFSD: nfsd4_close on file %.*s\n", 3248ca364317SJ.Bruce Fields (int)cstate->current_fh.fh_dentry->d_name.len, 3249ca364317SJ.Bruce Fields cstate->current_fh.fh_dentry->d_name.name); 32501da177e4SLinus Torvalds 32511da177e4SLinus Torvalds nfs4_lock_state(); 32521da177e4SLinus Torvalds /* check close_lru for replay */ 3253dd453dfdSBenny Halevy if ((status = nfs4_preprocess_seqid_op(cstate, 3254ca364317SJ.Bruce Fields close->cl_seqid, 32551da177e4SLinus Torvalds &close->cl_stateid, 3256f3362737SJ. Bruce Fields OPEN_STATE | CLOSE_STATE, 32571da177e4SLinus Torvalds &close->cl_stateowner, &stp, NULL))) 32581da177e4SLinus Torvalds goto out; 32591da177e4SLinus Torvalds status = nfs_ok; 32601da177e4SLinus Torvalds update_stateid(&stp->st_stateid); 32611da177e4SLinus Torvalds memcpy(&close->cl_stateid, &stp->st_stateid, sizeof(stateid_t)); 32621da177e4SLinus Torvalds 326304ef5954SJ. Bruce Fields /* release_stateid() calls nfsd_close() if needed */ 32642283963fSJ. Bruce Fields release_open_stateid(stp); 326504ef5954SJ. Bruce Fields 326604ef5954SJ. Bruce Fields /* place unused nfs4_stateowners on so_close_lru list to be 326704ef5954SJ. Bruce Fields * released by the laundromat service after the lease period 326804ef5954SJ. Bruce Fields * to enable us to handle CLOSE replay 326904ef5954SJ. Bruce Fields */ 327004ef5954SJ. Bruce Fields if (list_empty(&close->cl_stateowner->so_stateids)) 327104ef5954SJ. Bruce Fields move_to_close_lru(close->cl_stateowner); 32721da177e4SLinus Torvalds out: 3273f2327d9aSNeil Brown if (close->cl_stateowner) { 32741da177e4SLinus Torvalds nfs4_get_stateowner(close->cl_stateowner); 3275a4f1706aSJ.Bruce Fields cstate->replay_owner = close->cl_stateowner; 3276f2327d9aSNeil Brown } 32771da177e4SLinus Torvalds nfs4_unlock_state(); 32781da177e4SLinus Torvalds return status; 32791da177e4SLinus Torvalds } 32801da177e4SLinus Torvalds 3281b37ad28bSAl Viro __be32 3282ca364317SJ.Bruce Fields nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 3283ca364317SJ.Bruce Fields struct nfsd4_delegreturn *dr) 32841da177e4SLinus Torvalds { 3285203a8c8eSJ. Bruce Fields struct nfs4_delegation *dp; 3286203a8c8eSJ. Bruce Fields stateid_t *stateid = &dr->dr_stateid; 3287203a8c8eSJ. Bruce Fields struct inode *inode; 3288b37ad28bSAl Viro __be32 status; 32896668958fSAndy Adamson int flags = 0; 32901da177e4SLinus Torvalds 3291ca364317SJ.Bruce Fields if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) 3292203a8c8eSJ. Bruce Fields return status; 3293203a8c8eSJ. Bruce Fields inode = cstate->current_fh.fh_dentry->d_inode; 32941da177e4SLinus Torvalds 32956668958fSAndy Adamson if (nfsd4_has_session(cstate)) 32966668958fSAndy Adamson flags |= HAS_SESSION; 32971da177e4SLinus Torvalds nfs4_lock_state(); 3298203a8c8eSJ. Bruce Fields status = nfserr_bad_stateid; 3299203a8c8eSJ. Bruce Fields if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) 3300203a8c8eSJ. Bruce Fields goto out; 3301203a8c8eSJ. Bruce Fields status = nfserr_stale_stateid; 3302203a8c8eSJ. Bruce Fields if (STALE_STATEID(stateid)) 3303203a8c8eSJ. Bruce Fields goto out; 33047e0f7cf5SJ. Bruce Fields status = nfserr_bad_stateid; 3305203a8c8eSJ. Bruce Fields if (!is_delegation_stateid(stateid)) 3306203a8c8eSJ. Bruce Fields goto out; 3307203a8c8eSJ. Bruce Fields dp = find_delegation_stateid(inode, stateid); 3308e4e83ea4SJ. Bruce Fields if (!dp) 3309203a8c8eSJ. Bruce Fields goto out; 33106668958fSAndy Adamson status = check_stateid_generation(stateid, &dp->dl_stateid, flags); 3311203a8c8eSJ. Bruce Fields if (status) 3312203a8c8eSJ. Bruce Fields goto out; 3313203a8c8eSJ. Bruce Fields renew_client(dp->dl_client); 3314203a8c8eSJ. Bruce Fields 3315203a8c8eSJ. Bruce Fields unhash_delegation(dp); 33161da177e4SLinus Torvalds out: 3317203a8c8eSJ. Bruce Fields nfs4_unlock_state(); 3318203a8c8eSJ. Bruce Fields 33191da177e4SLinus Torvalds return status; 33201da177e4SLinus Torvalds } 33211da177e4SLinus Torvalds 33221da177e4SLinus Torvalds 33231da177e4SLinus Torvalds /* 33241da177e4SLinus Torvalds * Lock owner state (byte-range locks) 33251da177e4SLinus Torvalds */ 33261da177e4SLinus Torvalds #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) 33271da177e4SLinus Torvalds #define LOCK_HASH_BITS 8 33281da177e4SLinus Torvalds #define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS) 33291da177e4SLinus Torvalds #define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1) 33301da177e4SLinus Torvalds 333187df4de8SBenny Halevy static inline u64 333287df4de8SBenny Halevy end_offset(u64 start, u64 len) 333387df4de8SBenny Halevy { 333487df4de8SBenny Halevy u64 end; 333587df4de8SBenny Halevy 333687df4de8SBenny Halevy end = start + len; 333787df4de8SBenny Halevy return end >= start ? end: NFS4_MAX_UINT64; 333887df4de8SBenny Halevy } 333987df4de8SBenny Halevy 334087df4de8SBenny Halevy /* last octet in a range */ 334187df4de8SBenny Halevy static inline u64 334287df4de8SBenny Halevy last_byte_offset(u64 start, u64 len) 334387df4de8SBenny Halevy { 334487df4de8SBenny Halevy u64 end; 334587df4de8SBenny Halevy 334687df4de8SBenny Halevy BUG_ON(!len); 334787df4de8SBenny Halevy end = start + len; 334887df4de8SBenny Halevy return end > start ? end - 1: NFS4_MAX_UINT64; 334987df4de8SBenny Halevy } 335087df4de8SBenny Halevy 33511da177e4SLinus Torvalds #define lockownerid_hashval(id) \ 33521da177e4SLinus Torvalds ((id) & LOCK_HASH_MASK) 33531da177e4SLinus Torvalds 33541da177e4SLinus Torvalds static inline unsigned int 33551da177e4SLinus Torvalds lock_ownerstr_hashval(struct inode *inode, u32 cl_id, 33561da177e4SLinus Torvalds struct xdr_netobj *ownername) 33571da177e4SLinus Torvalds { 33581da177e4SLinus Torvalds return (file_hashval(inode) + cl_id 33591da177e4SLinus Torvalds + opaque_hashval(ownername->data, ownername->len)) 33601da177e4SLinus Torvalds & LOCK_HASH_MASK; 33611da177e4SLinus Torvalds } 33621da177e4SLinus Torvalds 33631da177e4SLinus Torvalds static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; 33641da177e4SLinus Torvalds static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; 33651da177e4SLinus Torvalds static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; 33661da177e4SLinus Torvalds 3367fd39ca9aSNeilBrown static struct nfs4_stateid * 33681da177e4SLinus Torvalds find_stateid(stateid_t *stid, int flags) 33691da177e4SLinus Torvalds { 33709346eff0SKrishna Kumar struct nfs4_stateid *local; 33711da177e4SLinus Torvalds u32 st_id = stid->si_stateownerid; 33721da177e4SLinus Torvalds u32 f_id = stid->si_fileid; 33731da177e4SLinus Torvalds unsigned int hashval; 33741da177e4SLinus Torvalds 33751da177e4SLinus Torvalds dprintk("NFSD: find_stateid flags 0x%x\n",flags); 33769346eff0SKrishna Kumar if (flags & (LOCK_STATE | RD_STATE | WR_STATE)) { 33771da177e4SLinus Torvalds hashval = stateid_hashval(st_id, f_id); 33781da177e4SLinus Torvalds list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { 33791da177e4SLinus Torvalds if ((local->st_stateid.si_stateownerid == st_id) && 33801da177e4SLinus Torvalds (local->st_stateid.si_fileid == f_id)) 33811da177e4SLinus Torvalds return local; 33821da177e4SLinus Torvalds } 33831da177e4SLinus Torvalds } 33849346eff0SKrishna Kumar 33859346eff0SKrishna Kumar if (flags & (OPEN_STATE | RD_STATE | WR_STATE)) { 33861da177e4SLinus Torvalds hashval = stateid_hashval(st_id, f_id); 33871da177e4SLinus Torvalds list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { 33881da177e4SLinus Torvalds if ((local->st_stateid.si_stateownerid == st_id) && 33891da177e4SLinus Torvalds (local->st_stateid.si_fileid == f_id)) 33901da177e4SLinus Torvalds return local; 33911da177e4SLinus Torvalds } 3392849823c5SNeil Brown } 33931da177e4SLinus Torvalds return NULL; 33941da177e4SLinus Torvalds } 33951da177e4SLinus Torvalds 33961da177e4SLinus Torvalds static struct nfs4_delegation * 33971da177e4SLinus Torvalds find_delegation_stateid(struct inode *ino, stateid_t *stid) 33981da177e4SLinus Torvalds { 339913cd2184SNeilBrown struct nfs4_file *fp; 340013cd2184SNeilBrown struct nfs4_delegation *dl; 34011da177e4SLinus Torvalds 34028c10cbdbSBenny Halevy dprintk("NFSD: %s: stateid=" STATEID_FMT "\n", __func__, 34038c10cbdbSBenny Halevy STATEID_VAL(stid)); 34041da177e4SLinus Torvalds 34051da177e4SLinus Torvalds fp = find_file(ino); 340613cd2184SNeilBrown if (!fp) 34071da177e4SLinus Torvalds return NULL; 340813cd2184SNeilBrown dl = find_delegation_file(fp, stid); 340913cd2184SNeilBrown put_nfs4_file(fp); 341013cd2184SNeilBrown return dl; 34111da177e4SLinus Torvalds } 34121da177e4SLinus Torvalds 34131da177e4SLinus Torvalds /* 34141da177e4SLinus Torvalds * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that 34151da177e4SLinus Torvalds * we can't properly handle lock requests that go beyond the (2^63 - 1)-th 34161da177e4SLinus Torvalds * byte, because of sign extension problems. Since NFSv4 calls for 64-bit 34171da177e4SLinus Torvalds * locking, this prevents us from being completely protocol-compliant. The 34181da177e4SLinus Torvalds * real solution to this problem is to start using unsigned file offsets in 34191da177e4SLinus Torvalds * the VFS, but this is a very deep change! 34201da177e4SLinus Torvalds */ 34211da177e4SLinus Torvalds static inline void 34221da177e4SLinus Torvalds nfs4_transform_lock_offset(struct file_lock *lock) 34231da177e4SLinus Torvalds { 34241da177e4SLinus Torvalds if (lock->fl_start < 0) 34251da177e4SLinus Torvalds lock->fl_start = OFFSET_MAX; 34261da177e4SLinus Torvalds if (lock->fl_end < 0) 34271da177e4SLinus Torvalds lock->fl_end = OFFSET_MAX; 34281da177e4SLinus Torvalds } 34291da177e4SLinus Torvalds 3430d5b9026aSNeilBrown /* Hack!: For now, we're defining this just so we can use a pointer to it 3431d5b9026aSNeilBrown * as a unique cookie to identify our (NFSv4's) posix locks. */ 34327b021967SAlexey Dobriyan static const struct lock_manager_operations nfsd_posix_mng_ops = { 3433d5b9026aSNeilBrown }; 34341da177e4SLinus Torvalds 34351da177e4SLinus Torvalds static inline void 34361da177e4SLinus Torvalds nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) 34371da177e4SLinus Torvalds { 3438d5b9026aSNeilBrown struct nfs4_stateowner *sop; 34391da177e4SLinus Torvalds 3440d5b9026aSNeilBrown if (fl->fl_lmops == &nfsd_posix_mng_ops) { 3441d5b9026aSNeilBrown sop = (struct nfs4_stateowner *) fl->fl_owner; 34421da177e4SLinus Torvalds kref_get(&sop->so_ref); 34431da177e4SLinus Torvalds deny->ld_sop = sop; 34441da177e4SLinus Torvalds deny->ld_clientid = sop->so_client->cl_clientid; 3445d5b9026aSNeilBrown } else { 3446d5b9026aSNeilBrown deny->ld_sop = NULL; 3447d5b9026aSNeilBrown deny->ld_clientid.cl_boot = 0; 3448d5b9026aSNeilBrown deny->ld_clientid.cl_id = 0; 34491da177e4SLinus Torvalds } 34501da177e4SLinus Torvalds deny->ld_start = fl->fl_start; 345187df4de8SBenny Halevy deny->ld_length = NFS4_MAX_UINT64; 345287df4de8SBenny Halevy if (fl->fl_end != NFS4_MAX_UINT64) 34531da177e4SLinus Torvalds deny->ld_length = fl->fl_end - fl->fl_start + 1; 34541da177e4SLinus Torvalds deny->ld_type = NFS4_READ_LT; 34551da177e4SLinus Torvalds if (fl->fl_type != F_RDLCK) 34561da177e4SLinus Torvalds deny->ld_type = NFS4_WRITE_LT; 34571da177e4SLinus Torvalds } 34581da177e4SLinus Torvalds 34591da177e4SLinus Torvalds static struct nfs4_stateowner * 34601da177e4SLinus Torvalds find_lockstateowner_str(struct inode *inode, clientid_t *clid, 34611da177e4SLinus Torvalds struct xdr_netobj *owner) 34621da177e4SLinus Torvalds { 34631da177e4SLinus Torvalds unsigned int hashval = lock_ownerstr_hashval(inode, clid->cl_id, owner); 34641da177e4SLinus Torvalds struct nfs4_stateowner *op; 34651da177e4SLinus Torvalds 34661da177e4SLinus Torvalds list_for_each_entry(op, &lock_ownerstr_hashtbl[hashval], so_strhash) { 3467599e0a22SJ. Bruce Fields if (same_owner_str(op, owner, clid)) 34681da177e4SLinus Torvalds return op; 34691da177e4SLinus Torvalds } 34701da177e4SLinus Torvalds return NULL; 34711da177e4SLinus Torvalds } 34721da177e4SLinus Torvalds 34731da177e4SLinus Torvalds /* 34741da177e4SLinus Torvalds * Alloc a lock owner structure. 34751da177e4SLinus Torvalds * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has 34761da177e4SLinus Torvalds * occured. 34771da177e4SLinus Torvalds * 34781da177e4SLinus Torvalds * strhashval = lock_ownerstr_hashval 34791da177e4SLinus Torvalds */ 34801da177e4SLinus Torvalds 34811da177e4SLinus Torvalds static struct nfs4_stateowner * 34821da177e4SLinus Torvalds alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) { 34831da177e4SLinus Torvalds struct nfs4_stateowner *sop; 34841da177e4SLinus Torvalds struct nfs4_replay *rp; 34851da177e4SLinus Torvalds unsigned int idhashval; 34861da177e4SLinus Torvalds 34871da177e4SLinus Torvalds if (!(sop = alloc_stateowner(&lock->lk_new_owner))) 34881da177e4SLinus Torvalds return NULL; 34891da177e4SLinus Torvalds idhashval = lockownerid_hashval(current_ownerid); 34901da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_idhash); 34911da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_strhash); 34921da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_perclient); 3493ea1da636SNeilBrown INIT_LIST_HEAD(&sop->so_stateids); 3494ea1da636SNeilBrown INIT_LIST_HEAD(&sop->so_perstateid); 34951da177e4SLinus Torvalds INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ 34961da177e4SLinus Torvalds sop->so_time = 0; 34971da177e4SLinus Torvalds list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); 34981da177e4SLinus Torvalds list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); 3499ea1da636SNeilBrown list_add(&sop->so_perstateid, &open_stp->st_lockowners); 35001da177e4SLinus Torvalds sop->so_is_open_owner = 0; 35011da177e4SLinus Torvalds sop->so_id = current_ownerid++; 35021da177e4SLinus Torvalds sop->so_client = clp; 3503b59e3c0eSNeil Brown /* It is the openowner seqid that will be incremented in encode in the 3504b59e3c0eSNeil Brown * case of new lockowners; so increment the lock seqid manually: */ 3505b59e3c0eSNeil Brown sop->so_seqid = lock->lk_new_lock_seqid + 1; 35061da177e4SLinus Torvalds sop->so_confirmed = 1; 35071da177e4SLinus Torvalds rp = &sop->so_replay; 3508de1ae286SAl Viro rp->rp_status = nfserr_serverfault; 35091da177e4SLinus Torvalds rp->rp_buflen = 0; 35101da177e4SLinus Torvalds rp->rp_buf = rp->rp_ibuf; 35111da177e4SLinus Torvalds return sop; 35121da177e4SLinus Torvalds } 35131da177e4SLinus Torvalds 3514fd39ca9aSNeilBrown static struct nfs4_stateid * 35151da177e4SLinus Torvalds alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp) 35161da177e4SLinus Torvalds { 35171da177e4SLinus Torvalds struct nfs4_stateid *stp; 35181da177e4SLinus Torvalds unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); 35191da177e4SLinus Torvalds 35205ac049acSNeilBrown stp = nfs4_alloc_stateid(); 35215ac049acSNeilBrown if (stp == NULL) 35221da177e4SLinus Torvalds goto out; 35231da177e4SLinus Torvalds INIT_LIST_HEAD(&stp->st_hash); 35241da177e4SLinus Torvalds INIT_LIST_HEAD(&stp->st_perfile); 3525ea1da636SNeilBrown INIT_LIST_HEAD(&stp->st_perstateowner); 3526ea1da636SNeilBrown INIT_LIST_HEAD(&stp->st_lockowners); /* not used */ 35271da177e4SLinus Torvalds list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); 35288beefa24SNeilBrown list_add(&stp->st_perfile, &fp->fi_stateids); 3529ea1da636SNeilBrown list_add(&stp->st_perstateowner, &sop->so_stateids); 35301da177e4SLinus Torvalds stp->st_stateowner = sop; 353113cd2184SNeilBrown get_nfs4_file(fp); 35321da177e4SLinus Torvalds stp->st_file = fp; 3533e4e83ea4SJ. Bruce Fields stp->st_stateid.si_boot = boot_time; 35341da177e4SLinus Torvalds stp->st_stateid.si_stateownerid = sop->so_id; 35351da177e4SLinus Torvalds stp->st_stateid.si_fileid = fp->fi_id; 35361da177e4SLinus Torvalds stp->st_stateid.si_generation = 0; 35371da177e4SLinus Torvalds stp->st_deny_bmap = open_stp->st_deny_bmap; 35384c4cd222SNeilBrown stp->st_openstp = open_stp; 35391da177e4SLinus Torvalds 35401da177e4SLinus Torvalds out: 35411da177e4SLinus Torvalds return stp; 35421da177e4SLinus Torvalds } 35431da177e4SLinus Torvalds 3544fd39ca9aSNeilBrown static int 35451da177e4SLinus Torvalds check_lock_length(u64 offset, u64 length) 35461da177e4SLinus Torvalds { 354787df4de8SBenny Halevy return ((length == 0) || ((length != NFS4_MAX_UINT64) && 35481da177e4SLinus Torvalds LOFF_OVERFLOW(offset, length))); 35491da177e4SLinus Torvalds } 35501da177e4SLinus Torvalds 35511da177e4SLinus Torvalds /* 35521da177e4SLinus Torvalds * LOCK operation 35531da177e4SLinus Torvalds */ 3554b37ad28bSAl Viro __be32 3555ca364317SJ.Bruce Fields nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 3556a4f1706aSJ.Bruce Fields struct nfsd4_lock *lock) 35571da177e4SLinus Torvalds { 35583e9e3dbeSNeilBrown struct nfs4_stateowner *open_sop = NULL; 3559b59e3c0eSNeil Brown struct nfs4_stateowner *lock_sop = NULL; 35601da177e4SLinus Torvalds struct nfs4_stateid *lock_stp; 35617d947842SJ. Bruce Fields struct nfs4_file *fp; 35627d947842SJ. Bruce Fields struct file *filp = NULL; 35631da177e4SLinus Torvalds struct file_lock file_lock; 35648dc7c311SAndy Adamson struct file_lock conflock; 3565b37ad28bSAl Viro __be32 status = 0; 35661da177e4SLinus Torvalds unsigned int strhashval; 3567150b3934SMarc Eshel unsigned int cmd; 3568b8dd7b9aSAl Viro int err; 35691da177e4SLinus Torvalds 35701da177e4SLinus Torvalds dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", 35711da177e4SLinus Torvalds (long long) lock->lk_offset, 35721da177e4SLinus Torvalds (long long) lock->lk_length); 35731da177e4SLinus Torvalds 35741da177e4SLinus Torvalds if (check_lock_length(lock->lk_offset, lock->lk_length)) 35751da177e4SLinus Torvalds return nfserr_inval; 35761da177e4SLinus Torvalds 3577ca364317SJ.Bruce Fields if ((status = fh_verify(rqstp, &cstate->current_fh, 35788837abcaSMiklos Szeredi S_IFREG, NFSD_MAY_LOCK))) { 3579a6f6ef2fSAndy Adamson dprintk("NFSD: nfsd4_lock: permission denied!\n"); 3580a6f6ef2fSAndy Adamson return status; 3581a6f6ef2fSAndy Adamson } 3582a6f6ef2fSAndy Adamson 35831da177e4SLinus Torvalds nfs4_lock_state(); 35841da177e4SLinus Torvalds 35851da177e4SLinus Torvalds if (lock->lk_is_new) { 35861da177e4SLinus Torvalds /* 35871da177e4SLinus Torvalds * Client indicates that this is a new lockowner. 3588893f8770SNeilBrown * Use open owner and open stateid to create lock owner and 3589893f8770SNeilBrown * lock stateid. 35901da177e4SLinus Torvalds */ 35911da177e4SLinus Torvalds struct nfs4_stateid *open_stp = NULL; 35921da177e4SLinus Torvalds 35931da177e4SLinus Torvalds status = nfserr_stale_clientid; 359460adfc50SAndy Adamson if (!nfsd4_has_session(cstate) && 359560adfc50SAndy Adamson STALE_CLIENTID(&lock->lk_new_clientid)) 35961da177e4SLinus Torvalds goto out; 35971da177e4SLinus Torvalds 35981da177e4SLinus Torvalds /* validate and update open stateid and open seqid */ 3599dd453dfdSBenny Halevy status = nfs4_preprocess_seqid_op(cstate, 36001da177e4SLinus Torvalds lock->lk_new_open_seqid, 36011da177e4SLinus Torvalds &lock->lk_new_open_stateid, 3602f3362737SJ. Bruce Fields OPEN_STATE, 36033a65588aSJ. Bruce Fields &lock->lk_replay_owner, &open_stp, 3604b59e3c0eSNeil Brown lock); 360537515177SNeilBrown if (status) 36061da177e4SLinus Torvalds goto out; 36073a65588aSJ. Bruce Fields open_sop = lock->lk_replay_owner; 36081da177e4SLinus Torvalds /* create lockowner and lock stateid */ 36091da177e4SLinus Torvalds fp = open_stp->st_file; 36101da177e4SLinus Torvalds strhashval = lock_ownerstr_hashval(fp->fi_inode, 36111da177e4SLinus Torvalds open_sop->so_client->cl_clientid.cl_id, 36121da177e4SLinus Torvalds &lock->v.new.owner); 36133e9e3dbeSNeilBrown /* XXX: Do we need to check for duplicate stateowners on 36143e9e3dbeSNeilBrown * the same file, or should they just be allowed (and 36153e9e3dbeSNeilBrown * create new stateids)? */ 36161da177e4SLinus Torvalds status = nfserr_resource; 3617b59e3c0eSNeil Brown lock_sop = alloc_init_lock_stateowner(strhashval, 3618b59e3c0eSNeil Brown open_sop->so_client, open_stp, lock); 3619b59e3c0eSNeil Brown if (lock_sop == NULL) 36201da177e4SLinus Torvalds goto out; 3621b59e3c0eSNeil Brown lock_stp = alloc_init_lock_stateid(lock_sop, fp, open_stp); 36228a280510SJ. Bruce Fields if (lock_stp == NULL) 36231da177e4SLinus Torvalds goto out; 36241da177e4SLinus Torvalds } else { 36251da177e4SLinus Torvalds /* lock (lock owner + lock stateid) already exists */ 3626dd453dfdSBenny Halevy status = nfs4_preprocess_seqid_op(cstate, 36271da177e4SLinus Torvalds lock->lk_old_lock_seqid, 36281da177e4SLinus Torvalds &lock->lk_old_lock_stateid, 3629f3362737SJ. Bruce Fields LOCK_STATE, 36303a65588aSJ. Bruce Fields &lock->lk_replay_owner, &lock_stp, lock); 36311da177e4SLinus Torvalds if (status) 36321da177e4SLinus Torvalds goto out; 36333a65588aSJ. Bruce Fields lock_sop = lock->lk_replay_owner; 36347d947842SJ. Bruce Fields fp = lock_stp->st_file; 36351da177e4SLinus Torvalds } 36363a65588aSJ. Bruce Fields /* lock->lk_replay_owner and lock_stp have been created or found */ 36371da177e4SLinus Torvalds 36380dd395dcSNeilBrown status = nfserr_grace; 3639af558e33SJ. Bruce Fields if (locks_in_grace() && !lock->lk_reclaim) 36400dd395dcSNeilBrown goto out; 36410dd395dcSNeilBrown status = nfserr_no_grace; 3642af558e33SJ. Bruce Fields if (!locks_in_grace() && lock->lk_reclaim) 36430dd395dcSNeilBrown goto out; 36440dd395dcSNeilBrown 36451da177e4SLinus Torvalds locks_init_lock(&file_lock); 36461da177e4SLinus Torvalds switch (lock->lk_type) { 36471da177e4SLinus Torvalds case NFS4_READ_LT: 36481da177e4SLinus Torvalds case NFS4_READW_LT: 36497d947842SJ. Bruce Fields if (find_readable_file(lock_stp->st_file)) { 36507d947842SJ. Bruce Fields nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_READ); 3651f9d7562fSJ. Bruce Fields filp = find_readable_file(lock_stp->st_file); 36527d947842SJ. Bruce Fields } 36531da177e4SLinus Torvalds file_lock.fl_type = F_RDLCK; 3654150b3934SMarc Eshel cmd = F_SETLK; 36551da177e4SLinus Torvalds break; 36561da177e4SLinus Torvalds case NFS4_WRITE_LT: 36571da177e4SLinus Torvalds case NFS4_WRITEW_LT: 36587d947842SJ. Bruce Fields if (find_writeable_file(lock_stp->st_file)) { 36597d947842SJ. Bruce Fields nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_WRITE); 3660f9d7562fSJ. Bruce Fields filp = find_writeable_file(lock_stp->st_file); 36617d947842SJ. Bruce Fields } 36621da177e4SLinus Torvalds file_lock.fl_type = F_WRLCK; 3663150b3934SMarc Eshel cmd = F_SETLK; 36641da177e4SLinus Torvalds break; 36651da177e4SLinus Torvalds default: 36661da177e4SLinus Torvalds status = nfserr_inval; 36671da177e4SLinus Torvalds goto out; 36681da177e4SLinus Torvalds } 3669f9d7562fSJ. Bruce Fields if (!filp) { 3670f9d7562fSJ. Bruce Fields status = nfserr_openmode; 3671f9d7562fSJ. Bruce Fields goto out; 3672f9d7562fSJ. Bruce Fields } 3673b59e3c0eSNeil Brown file_lock.fl_owner = (fl_owner_t)lock_sop; 36741da177e4SLinus Torvalds file_lock.fl_pid = current->tgid; 36751da177e4SLinus Torvalds file_lock.fl_file = filp; 36761da177e4SLinus Torvalds file_lock.fl_flags = FL_POSIX; 3677d5b9026aSNeilBrown file_lock.fl_lmops = &nfsd_posix_mng_ops; 36781da177e4SLinus Torvalds 36791da177e4SLinus Torvalds file_lock.fl_start = lock->lk_offset; 368087df4de8SBenny Halevy file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); 36811da177e4SLinus Torvalds nfs4_transform_lock_offset(&file_lock); 36821da177e4SLinus Torvalds 36831da177e4SLinus Torvalds /* 36841da177e4SLinus Torvalds * Try to lock the file in the VFS. 36851da177e4SLinus Torvalds * Note: locks.c uses the BKL to protect the inode's lock list. 36861da177e4SLinus Torvalds */ 36871da177e4SLinus Torvalds 3688fd85b817SMarc Eshel err = vfs_lock_file(filp, cmd, &file_lock, &conflock); 3689b8dd7b9aSAl Viro switch (-err) { 36901da177e4SLinus Torvalds case 0: /* success! */ 36911da177e4SLinus Torvalds update_stateid(&lock_stp->st_stateid); 36921da177e4SLinus Torvalds memcpy(&lock->lk_resp_stateid, &lock_stp->st_stateid, 36931da177e4SLinus Torvalds sizeof(stateid_t)); 3694b8dd7b9aSAl Viro status = 0; 3695eb76b3fdSAndy Adamson break; 3696eb76b3fdSAndy Adamson case (EAGAIN): /* conflock holds conflicting lock */ 3697eb76b3fdSAndy Adamson status = nfserr_denied; 3698eb76b3fdSAndy Adamson dprintk("NFSD: nfsd4_lock: conflicting lock found!\n"); 3699eb76b3fdSAndy Adamson nfs4_set_lock_denied(&conflock, &lock->lk_denied); 3700eb76b3fdSAndy Adamson break; 37011da177e4SLinus Torvalds case (EDEADLK): 37021da177e4SLinus Torvalds status = nfserr_deadlock; 3703eb76b3fdSAndy Adamson break; 37041da177e4SLinus Torvalds default: 3705fd85b817SMarc Eshel dprintk("NFSD: nfsd4_lock: vfs_lock_file() failed! status %d\n",err); 3706eb76b3fdSAndy Adamson status = nfserr_resource; 3707eb76b3fdSAndy Adamson break; 37081da177e4SLinus Torvalds } 37091da177e4SLinus Torvalds out: 37108a280510SJ. Bruce Fields if (status && lock->lk_is_new && lock_sop) 3711f044ff83SJ. Bruce Fields release_lockowner(lock_sop); 37123a65588aSJ. Bruce Fields if (lock->lk_replay_owner) { 37133a65588aSJ. Bruce Fields nfs4_get_stateowner(lock->lk_replay_owner); 3714a4f1706aSJ.Bruce Fields cstate->replay_owner = lock->lk_replay_owner; 3715f2327d9aSNeil Brown } 37161da177e4SLinus Torvalds nfs4_unlock_state(); 37171da177e4SLinus Torvalds return status; 37181da177e4SLinus Torvalds } 37191da177e4SLinus Torvalds 37201da177e4SLinus Torvalds /* 372155ef1274SJ. Bruce Fields * The NFSv4 spec allows a client to do a LOCKT without holding an OPEN, 372255ef1274SJ. Bruce Fields * so we do a temporary open here just to get an open file to pass to 372355ef1274SJ. Bruce Fields * vfs_test_lock. (Arguably perhaps test_lock should be done with an 372455ef1274SJ. Bruce Fields * inode operation.) 372555ef1274SJ. Bruce Fields */ 372655ef1274SJ. Bruce Fields static int nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock) 372755ef1274SJ. Bruce Fields { 372855ef1274SJ. Bruce Fields struct file *file; 372955ef1274SJ. Bruce Fields int err; 373055ef1274SJ. Bruce Fields 373155ef1274SJ. Bruce Fields err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); 373255ef1274SJ. Bruce Fields if (err) 373355ef1274SJ. Bruce Fields return err; 373455ef1274SJ. Bruce Fields err = vfs_test_lock(file, lock); 373555ef1274SJ. Bruce Fields nfsd_close(file); 373655ef1274SJ. Bruce Fields return err; 373755ef1274SJ. Bruce Fields } 373855ef1274SJ. Bruce Fields 373955ef1274SJ. Bruce Fields /* 37401da177e4SLinus Torvalds * LOCKT operation 37411da177e4SLinus Torvalds */ 3742b37ad28bSAl Viro __be32 3743ca364317SJ.Bruce Fields nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 3744ca364317SJ.Bruce Fields struct nfsd4_lockt *lockt) 37451da177e4SLinus Torvalds { 37461da177e4SLinus Torvalds struct inode *inode; 37471da177e4SLinus Torvalds struct file_lock file_lock; 3748fd85b817SMarc Eshel int error; 3749b37ad28bSAl Viro __be32 status; 37501da177e4SLinus Torvalds 3751af558e33SJ. Bruce Fields if (locks_in_grace()) 37521da177e4SLinus Torvalds return nfserr_grace; 37531da177e4SLinus Torvalds 37541da177e4SLinus Torvalds if (check_lock_length(lockt->lt_offset, lockt->lt_length)) 37551da177e4SLinus Torvalds return nfserr_inval; 37561da177e4SLinus Torvalds 37571da177e4SLinus Torvalds lockt->lt_stateowner = NULL; 37581da177e4SLinus Torvalds nfs4_lock_state(); 37591da177e4SLinus Torvalds 37601da177e4SLinus Torvalds status = nfserr_stale_clientid; 376160adfc50SAndy Adamson if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid)) 37621da177e4SLinus Torvalds goto out; 37631da177e4SLinus Torvalds 3764ca364317SJ.Bruce Fields if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) { 3765849823c5SNeil Brown dprintk("NFSD: nfsd4_lockt: fh_verify() failed!\n"); 37661da177e4SLinus Torvalds if (status == nfserr_symlink) 37671da177e4SLinus Torvalds status = nfserr_inval; 37681da177e4SLinus Torvalds goto out; 37691da177e4SLinus Torvalds } 37701da177e4SLinus Torvalds 3771ca364317SJ.Bruce Fields inode = cstate->current_fh.fh_dentry->d_inode; 37721da177e4SLinus Torvalds locks_init_lock(&file_lock); 37731da177e4SLinus Torvalds switch (lockt->lt_type) { 37741da177e4SLinus Torvalds case NFS4_READ_LT: 37751da177e4SLinus Torvalds case NFS4_READW_LT: 37761da177e4SLinus Torvalds file_lock.fl_type = F_RDLCK; 37771da177e4SLinus Torvalds break; 37781da177e4SLinus Torvalds case NFS4_WRITE_LT: 37791da177e4SLinus Torvalds case NFS4_WRITEW_LT: 37801da177e4SLinus Torvalds file_lock.fl_type = F_WRLCK; 37811da177e4SLinus Torvalds break; 37821da177e4SLinus Torvalds default: 37832fdada03SJ. Bruce Fields dprintk("NFSD: nfs4_lockt: bad lock type!\n"); 37841da177e4SLinus Torvalds status = nfserr_inval; 37851da177e4SLinus Torvalds goto out; 37861da177e4SLinus Torvalds } 37871da177e4SLinus Torvalds 37881da177e4SLinus Torvalds lockt->lt_stateowner = find_lockstateowner_str(inode, 37891da177e4SLinus Torvalds &lockt->lt_clientid, &lockt->lt_owner); 37901da177e4SLinus Torvalds if (lockt->lt_stateowner) 37911da177e4SLinus Torvalds file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner; 37921da177e4SLinus Torvalds file_lock.fl_pid = current->tgid; 37931da177e4SLinus Torvalds file_lock.fl_flags = FL_POSIX; 37941da177e4SLinus Torvalds 37951da177e4SLinus Torvalds file_lock.fl_start = lockt->lt_offset; 379687df4de8SBenny Halevy file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length); 37971da177e4SLinus Torvalds 37981da177e4SLinus Torvalds nfs4_transform_lock_offset(&file_lock); 37991da177e4SLinus Torvalds 38001da177e4SLinus Torvalds status = nfs_ok; 380155ef1274SJ. Bruce Fields error = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock); 3802fd85b817SMarc Eshel if (error) { 3803fd85b817SMarc Eshel status = nfserrno(error); 3804fd85b817SMarc Eshel goto out; 3805fd85b817SMarc Eshel } 38069d6a8c5cSMarc Eshel if (file_lock.fl_type != F_UNLCK) { 38071da177e4SLinus Torvalds status = nfserr_denied; 38089d6a8c5cSMarc Eshel nfs4_set_lock_denied(&file_lock, &lockt->lt_denied); 38091da177e4SLinus Torvalds } 38101da177e4SLinus Torvalds out: 38111da177e4SLinus Torvalds nfs4_unlock_state(); 38121da177e4SLinus Torvalds return status; 38131da177e4SLinus Torvalds } 38141da177e4SLinus Torvalds 3815b37ad28bSAl Viro __be32 3816ca364317SJ.Bruce Fields nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 3817a4f1706aSJ.Bruce Fields struct nfsd4_locku *locku) 38181da177e4SLinus Torvalds { 38191da177e4SLinus Torvalds struct nfs4_stateid *stp; 38201da177e4SLinus Torvalds struct file *filp = NULL; 38211da177e4SLinus Torvalds struct file_lock file_lock; 3822b37ad28bSAl Viro __be32 status; 3823b8dd7b9aSAl Viro int err; 38241da177e4SLinus Torvalds 38251da177e4SLinus Torvalds dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", 38261da177e4SLinus Torvalds (long long) locku->lu_offset, 38271da177e4SLinus Torvalds (long long) locku->lu_length); 38281da177e4SLinus Torvalds 38291da177e4SLinus Torvalds if (check_lock_length(locku->lu_offset, locku->lu_length)) 38301da177e4SLinus Torvalds return nfserr_inval; 38311da177e4SLinus Torvalds 38321da177e4SLinus Torvalds nfs4_lock_state(); 38331da177e4SLinus Torvalds 3834dd453dfdSBenny Halevy if ((status = nfs4_preprocess_seqid_op(cstate, 38351da177e4SLinus Torvalds locku->lu_seqid, 38361da177e4SLinus Torvalds &locku->lu_stateid, 3837f3362737SJ. Bruce Fields LOCK_STATE, 38381da177e4SLinus Torvalds &locku->lu_stateowner, &stp, NULL))) 38391da177e4SLinus Torvalds goto out; 38401da177e4SLinus Torvalds 3841f9d7562fSJ. Bruce Fields filp = find_any_file(stp->st_file); 3842f9d7562fSJ. Bruce Fields if (!filp) { 3843f9d7562fSJ. Bruce Fields status = nfserr_lock_range; 3844f9d7562fSJ. Bruce Fields goto out; 3845f9d7562fSJ. Bruce Fields } 38461da177e4SLinus Torvalds BUG_ON(!filp); 38471da177e4SLinus Torvalds locks_init_lock(&file_lock); 38481da177e4SLinus Torvalds file_lock.fl_type = F_UNLCK; 38491da177e4SLinus Torvalds file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; 38501da177e4SLinus Torvalds file_lock.fl_pid = current->tgid; 38511da177e4SLinus Torvalds file_lock.fl_file = filp; 38521da177e4SLinus Torvalds file_lock.fl_flags = FL_POSIX; 3853d5b9026aSNeilBrown file_lock.fl_lmops = &nfsd_posix_mng_ops; 38541da177e4SLinus Torvalds file_lock.fl_start = locku->lu_offset; 38551da177e4SLinus Torvalds 385687df4de8SBenny Halevy file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length); 38571da177e4SLinus Torvalds nfs4_transform_lock_offset(&file_lock); 38581da177e4SLinus Torvalds 38591da177e4SLinus Torvalds /* 38601da177e4SLinus Torvalds * Try to unlock the file in the VFS. 38611da177e4SLinus Torvalds */ 3862fd85b817SMarc Eshel err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL); 3863b8dd7b9aSAl Viro if (err) { 3864fd85b817SMarc Eshel dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); 38651da177e4SLinus Torvalds goto out_nfserr; 38661da177e4SLinus Torvalds } 38671da177e4SLinus Torvalds /* 38681da177e4SLinus Torvalds * OK, unlock succeeded; the only thing left to do is update the stateid. 38691da177e4SLinus Torvalds */ 38701da177e4SLinus Torvalds update_stateid(&stp->st_stateid); 38711da177e4SLinus Torvalds memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t)); 38721da177e4SLinus Torvalds 38731da177e4SLinus Torvalds out: 3874f2327d9aSNeil Brown if (locku->lu_stateowner) { 38751da177e4SLinus Torvalds nfs4_get_stateowner(locku->lu_stateowner); 3876a4f1706aSJ.Bruce Fields cstate->replay_owner = locku->lu_stateowner; 3877f2327d9aSNeil Brown } 38781da177e4SLinus Torvalds nfs4_unlock_state(); 38791da177e4SLinus Torvalds return status; 38801da177e4SLinus Torvalds 38811da177e4SLinus Torvalds out_nfserr: 3882b8dd7b9aSAl Viro status = nfserrno(err); 38831da177e4SLinus Torvalds goto out; 38841da177e4SLinus Torvalds } 38851da177e4SLinus Torvalds 38861da177e4SLinus Torvalds /* 38871da177e4SLinus Torvalds * returns 38881da177e4SLinus Torvalds * 1: locks held by lockowner 38891da177e4SLinus Torvalds * 0: no locks held by lockowner 38901da177e4SLinus Torvalds */ 38911da177e4SLinus Torvalds static int 3892f9d7562fSJ. Bruce Fields check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner) 38931da177e4SLinus Torvalds { 38941da177e4SLinus Torvalds struct file_lock **flpp; 3895f9d7562fSJ. Bruce Fields struct inode *inode = filp->fi_inode; 38961da177e4SLinus Torvalds int status = 0; 38971da177e4SLinus Torvalds 38981da177e4SLinus Torvalds lock_kernel(); 38991da177e4SLinus Torvalds for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { 3900796dadfdSJ. Bruce Fields if ((*flpp)->fl_owner == (fl_owner_t)lowner) { 39011da177e4SLinus Torvalds status = 1; 39021da177e4SLinus Torvalds goto out; 39031da177e4SLinus Torvalds } 3904796dadfdSJ. Bruce Fields } 39051da177e4SLinus Torvalds out: 39061da177e4SLinus Torvalds unlock_kernel(); 39071da177e4SLinus Torvalds return status; 39081da177e4SLinus Torvalds } 39091da177e4SLinus Torvalds 3910b37ad28bSAl Viro __be32 3911b591480bSJ.Bruce Fields nfsd4_release_lockowner(struct svc_rqst *rqstp, 3912b591480bSJ.Bruce Fields struct nfsd4_compound_state *cstate, 3913b591480bSJ.Bruce Fields struct nfsd4_release_lockowner *rlockowner) 39141da177e4SLinus Torvalds { 39151da177e4SLinus Torvalds clientid_t *clid = &rlockowner->rl_clientid; 39163e9e3dbeSNeilBrown struct nfs4_stateowner *sop; 39173e9e3dbeSNeilBrown struct nfs4_stateid *stp; 39181da177e4SLinus Torvalds struct xdr_netobj *owner = &rlockowner->rl_owner; 39193e9e3dbeSNeilBrown struct list_head matches; 39203e9e3dbeSNeilBrown int i; 3921b37ad28bSAl Viro __be32 status; 39221da177e4SLinus Torvalds 39231da177e4SLinus Torvalds dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", 39241da177e4SLinus Torvalds clid->cl_boot, clid->cl_id); 39251da177e4SLinus Torvalds 39261da177e4SLinus Torvalds /* XXX check for lease expiration */ 39271da177e4SLinus Torvalds 39281da177e4SLinus Torvalds status = nfserr_stale_clientid; 3929849823c5SNeil Brown if (STALE_CLIENTID(clid)) 39301da177e4SLinus Torvalds return status; 39311da177e4SLinus Torvalds 39321da177e4SLinus Torvalds nfs4_lock_state(); 39331da177e4SLinus Torvalds 39341da177e4SLinus Torvalds status = nfserr_locks_held; 39353e9e3dbeSNeilBrown /* XXX: we're doing a linear search through all the lockowners. 39363e9e3dbeSNeilBrown * Yipes! For now we'll just hope clients aren't really using 39373e9e3dbeSNeilBrown * release_lockowner much, but eventually we have to fix these 39383e9e3dbeSNeilBrown * data structures. */ 39393e9e3dbeSNeilBrown INIT_LIST_HEAD(&matches); 39403e9e3dbeSNeilBrown for (i = 0; i < LOCK_HASH_SIZE; i++) { 39413e9e3dbeSNeilBrown list_for_each_entry(sop, &lock_ownerid_hashtbl[i], so_idhash) { 3942599e0a22SJ. Bruce Fields if (!same_owner_str(sop, owner, clid)) 39433e9e3dbeSNeilBrown continue; 39443e9e3dbeSNeilBrown list_for_each_entry(stp, &sop->so_stateids, 3945ea1da636SNeilBrown st_perstateowner) { 3946f9d7562fSJ. Bruce Fields if (check_for_locks(stp->st_file, sop)) 39471da177e4SLinus Torvalds goto out; 39483e9e3dbeSNeilBrown /* Note: so_perclient unused for lockowners, 39493e9e3dbeSNeilBrown * so it's OK to fool with here. */ 39503e9e3dbeSNeilBrown list_add(&sop->so_perclient, &matches); 39511da177e4SLinus Torvalds } 39523e9e3dbeSNeilBrown } 39533e9e3dbeSNeilBrown } 39543e9e3dbeSNeilBrown /* Clients probably won't expect us to return with some (but not all) 39553e9e3dbeSNeilBrown * of the lockowner state released; so don't release any until all 39563e9e3dbeSNeilBrown * have been checked. */ 39571da177e4SLinus Torvalds status = nfs_ok; 39580fa822e4SNeilBrown while (!list_empty(&matches)) { 39590fa822e4SNeilBrown sop = list_entry(matches.next, struct nfs4_stateowner, 39600fa822e4SNeilBrown so_perclient); 39610fa822e4SNeilBrown /* unhash_stateowner deletes so_perclient only 39620fa822e4SNeilBrown * for openowners. */ 39630fa822e4SNeilBrown list_del(&sop->so_perclient); 3964f044ff83SJ. Bruce Fields release_lockowner(sop); 39651da177e4SLinus Torvalds } 39661da177e4SLinus Torvalds out: 39671da177e4SLinus Torvalds nfs4_unlock_state(); 39681da177e4SLinus Torvalds return status; 39691da177e4SLinus Torvalds } 39701da177e4SLinus Torvalds 39711da177e4SLinus Torvalds static inline struct nfs4_client_reclaim * 3972a55370a3SNeilBrown alloc_reclaim(void) 39731da177e4SLinus Torvalds { 3974a55370a3SNeilBrown return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); 39751da177e4SLinus Torvalds } 39761da177e4SLinus Torvalds 3977c7b9a459SNeilBrown int 3978a1bcecd2SAndy Adamson nfs4_has_reclaimed_state(const char *name, bool use_exchange_id) 3979c7b9a459SNeilBrown { 3980c7b9a459SNeilBrown unsigned int strhashval = clientstr_hashval(name); 3981c7b9a459SNeilBrown struct nfs4_client *clp; 3982c7b9a459SNeilBrown 3983a1bcecd2SAndy Adamson clp = find_confirmed_client_by_str(name, strhashval, use_exchange_id); 3984c7b9a459SNeilBrown return clp ? 1 : 0; 3985c7b9a459SNeilBrown } 3986c7b9a459SNeilBrown 39871da177e4SLinus Torvalds /* 39881da177e4SLinus Torvalds * failure => all reset bets are off, nfserr_no_grace... 39891da177e4SLinus Torvalds */ 3990190e4fbfSNeilBrown int 3991190e4fbfSNeilBrown nfs4_client_to_reclaim(const char *name) 39921da177e4SLinus Torvalds { 39931da177e4SLinus Torvalds unsigned int strhashval; 39941da177e4SLinus Torvalds struct nfs4_client_reclaim *crp = NULL; 39951da177e4SLinus Torvalds 3996a55370a3SNeilBrown dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); 3997a55370a3SNeilBrown crp = alloc_reclaim(); 39981da177e4SLinus Torvalds if (!crp) 39991da177e4SLinus Torvalds return 0; 4000a55370a3SNeilBrown strhashval = clientstr_hashval(name); 40011da177e4SLinus Torvalds INIT_LIST_HEAD(&crp->cr_strhash); 40021da177e4SLinus Torvalds list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); 4003a55370a3SNeilBrown memcpy(crp->cr_recdir, name, HEXDIR_LEN); 40041da177e4SLinus Torvalds reclaim_str_hashtbl_size++; 40051da177e4SLinus Torvalds return 1; 40061da177e4SLinus Torvalds } 40071da177e4SLinus Torvalds 40081da177e4SLinus Torvalds static void 40091da177e4SLinus Torvalds nfs4_release_reclaim(void) 40101da177e4SLinus Torvalds { 40111da177e4SLinus Torvalds struct nfs4_client_reclaim *crp = NULL; 40121da177e4SLinus Torvalds int i; 40131da177e4SLinus Torvalds 40141da177e4SLinus Torvalds for (i = 0; i < CLIENT_HASH_SIZE; i++) { 40151da177e4SLinus Torvalds while (!list_empty(&reclaim_str_hashtbl[i])) { 40161da177e4SLinus Torvalds crp = list_entry(reclaim_str_hashtbl[i].next, 40171da177e4SLinus Torvalds struct nfs4_client_reclaim, cr_strhash); 40181da177e4SLinus Torvalds list_del(&crp->cr_strhash); 40191da177e4SLinus Torvalds kfree(crp); 40201da177e4SLinus Torvalds reclaim_str_hashtbl_size--; 40211da177e4SLinus Torvalds } 40221da177e4SLinus Torvalds } 40231da177e4SLinus Torvalds BUG_ON(reclaim_str_hashtbl_size); 40241da177e4SLinus Torvalds } 40251da177e4SLinus Torvalds 40261da177e4SLinus Torvalds /* 40271da177e4SLinus Torvalds * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ 4028fd39ca9aSNeilBrown static struct nfs4_client_reclaim * 40291da177e4SLinus Torvalds nfs4_find_reclaim_client(clientid_t *clid) 40301da177e4SLinus Torvalds { 40311da177e4SLinus Torvalds unsigned int strhashval; 40321da177e4SLinus Torvalds struct nfs4_client *clp; 40331da177e4SLinus Torvalds struct nfs4_client_reclaim *crp = NULL; 40341da177e4SLinus Torvalds 40351da177e4SLinus Torvalds 40361da177e4SLinus Torvalds /* find clientid in conf_id_hashtbl */ 40371da177e4SLinus Torvalds clp = find_confirmed_client(clid); 40381da177e4SLinus Torvalds if (clp == NULL) 40391da177e4SLinus Torvalds return NULL; 40401da177e4SLinus Torvalds 4041a55370a3SNeilBrown dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", 4042a55370a3SNeilBrown clp->cl_name.len, clp->cl_name.data, 4043a55370a3SNeilBrown clp->cl_recdir); 40441da177e4SLinus Torvalds 40451da177e4SLinus Torvalds /* find clp->cl_name in reclaim_str_hashtbl */ 4046a55370a3SNeilBrown strhashval = clientstr_hashval(clp->cl_recdir); 40471da177e4SLinus Torvalds list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { 4048a55370a3SNeilBrown if (same_name(crp->cr_recdir, clp->cl_recdir)) { 40491da177e4SLinus Torvalds return crp; 40501da177e4SLinus Torvalds } 40511da177e4SLinus Torvalds } 40521da177e4SLinus Torvalds return NULL; 40531da177e4SLinus Torvalds } 40541da177e4SLinus Torvalds 40551da177e4SLinus Torvalds /* 40561da177e4SLinus Torvalds * Called from OPEN. Look for clientid in reclaim list. 40571da177e4SLinus Torvalds */ 4058b37ad28bSAl Viro __be32 40591da177e4SLinus Torvalds nfs4_check_open_reclaim(clientid_t *clid) 40601da177e4SLinus Torvalds { 4061dfc83565SNeilBrown return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; 40621da177e4SLinus Torvalds } 40631da177e4SLinus Torvalds 4064ac4d8ff2SNeilBrown /* initialization to perform at module load time: */ 40651da177e4SLinus Torvalds 4066e8ff2a84SJ. Bruce Fields int 4067ac4d8ff2SNeilBrown nfs4_state_init(void) 40681da177e4SLinus Torvalds { 4069e8ff2a84SJ. Bruce Fields int i, status; 40701da177e4SLinus Torvalds 4071e8ff2a84SJ. Bruce Fields status = nfsd4_init_slabs(); 4072e8ff2a84SJ. Bruce Fields if (status) 4073e8ff2a84SJ. Bruce Fields return status; 40741da177e4SLinus Torvalds for (i = 0; i < CLIENT_HASH_SIZE; i++) { 40751da177e4SLinus Torvalds INIT_LIST_HEAD(&conf_id_hashtbl[i]); 40761da177e4SLinus Torvalds INIT_LIST_HEAD(&conf_str_hashtbl[i]); 40771da177e4SLinus Torvalds INIT_LIST_HEAD(&unconf_str_hashtbl[i]); 40781da177e4SLinus Torvalds INIT_LIST_HEAD(&unconf_id_hashtbl[i]); 407902cb2858SWang Chen INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); 40801da177e4SLinus Torvalds } 40815282fd72SMarc Eshel for (i = 0; i < SESSION_HASH_SIZE; i++) 40825282fd72SMarc Eshel INIT_LIST_HEAD(&sessionid_hashtbl[i]); 40831da177e4SLinus Torvalds for (i = 0; i < FILE_HASH_SIZE; i++) { 40841da177e4SLinus Torvalds INIT_LIST_HEAD(&file_hashtbl[i]); 40851da177e4SLinus Torvalds } 40861da177e4SLinus Torvalds for (i = 0; i < OWNER_HASH_SIZE; i++) { 40871da177e4SLinus Torvalds INIT_LIST_HEAD(&ownerstr_hashtbl[i]); 40881da177e4SLinus Torvalds INIT_LIST_HEAD(&ownerid_hashtbl[i]); 40891da177e4SLinus Torvalds } 40901da177e4SLinus Torvalds for (i = 0; i < STATEID_HASH_SIZE; i++) { 40911da177e4SLinus Torvalds INIT_LIST_HEAD(&stateid_hashtbl[i]); 40921da177e4SLinus Torvalds INIT_LIST_HEAD(&lockstateid_hashtbl[i]); 40931da177e4SLinus Torvalds } 40941da177e4SLinus Torvalds for (i = 0; i < LOCK_HASH_SIZE; i++) { 40951da177e4SLinus Torvalds INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); 40961da177e4SLinus Torvalds INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); 40971da177e4SLinus Torvalds } 40981da177e4SLinus Torvalds memset(&onestateid, ~0, sizeof(stateid_t)); 40991da177e4SLinus Torvalds INIT_LIST_HEAD(&close_lru); 41001da177e4SLinus Torvalds INIT_LIST_HEAD(&client_lru); 41011da177e4SLinus Torvalds INIT_LIST_HEAD(&del_recall_lru); 4102ac4d8ff2SNeilBrown reclaim_str_hashtbl_size = 0; 4103e8ff2a84SJ. Bruce Fields return 0; 4104ac4d8ff2SNeilBrown } 4105ac4d8ff2SNeilBrown 4106190e4fbfSNeilBrown static void 4107190e4fbfSNeilBrown nfsd4_load_reboot_recovery_data(void) 4108190e4fbfSNeilBrown { 4109190e4fbfSNeilBrown int status; 4110190e4fbfSNeilBrown 41110964a3d3SNeilBrown nfs4_lock_state(); 41120964a3d3SNeilBrown nfsd4_init_recdir(user_recovery_dirname); 4113190e4fbfSNeilBrown status = nfsd4_recdir_load(); 41140964a3d3SNeilBrown nfs4_unlock_state(); 4115190e4fbfSNeilBrown if (status) 4116190e4fbfSNeilBrown printk("NFSD: Failure reading reboot recovery data\n"); 4117190e4fbfSNeilBrown } 4118190e4fbfSNeilBrown 4119c2f1a551SMeelap Shah /* 4120c2f1a551SMeelap Shah * Since the lifetime of a delegation isn't limited to that of an open, a 4121c2f1a551SMeelap Shah * client may quite reasonably hang on to a delegation as long as it has 4122c2f1a551SMeelap Shah * the inode cached. This becomes an obvious problem the first time a 4123c2f1a551SMeelap Shah * client's inode cache approaches the size of the server's total memory. 4124c2f1a551SMeelap Shah * 4125c2f1a551SMeelap Shah * For now we avoid this problem by imposing a hard limit on the number 4126c2f1a551SMeelap Shah * of delegations, which varies according to the server's memory size. 4127c2f1a551SMeelap Shah */ 4128c2f1a551SMeelap Shah static void 4129c2f1a551SMeelap Shah set_max_delegations(void) 4130c2f1a551SMeelap Shah { 4131c2f1a551SMeelap Shah /* 4132c2f1a551SMeelap Shah * Allow at most 4 delegations per megabyte of RAM. Quick 4133c2f1a551SMeelap Shah * estimates suggest that in the worst case (where every delegation 4134c2f1a551SMeelap Shah * is for a different inode), a delegation could take about 1.5K, 4135c2f1a551SMeelap Shah * giving a worst case usage of about 6% of memory. 4136c2f1a551SMeelap Shah */ 4137c2f1a551SMeelap Shah max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); 4138c2f1a551SMeelap Shah } 4139c2f1a551SMeelap Shah 4140ac4d8ff2SNeilBrown /* initialization to perform when the nfsd service is started: */ 4141ac4d8ff2SNeilBrown 414229ab23ccSJ. Bruce Fields static int 4143ac4d8ff2SNeilBrown __nfs4_state_start(void) 4144ac4d8ff2SNeilBrown { 4145b5a1a81eSJ. Bruce Fields int ret; 4146b5a1a81eSJ. Bruce Fields 41471da177e4SLinus Torvalds boot_time = get_seconds(); 4148af558e33SJ. Bruce Fields locks_start_grace(&nfsd4_manager); 41499a8db97eSMarc Eshel printk(KERN_INFO "NFSD: starting %ld-second grace period\n", 4150e46b498cSJ. Bruce Fields nfsd4_grace); 4151b5a1a81eSJ. Bruce Fields ret = set_callback_cred(); 4152b5a1a81eSJ. Bruce Fields if (ret) 4153b5a1a81eSJ. Bruce Fields return -ENOMEM; 415458da282bSNeilBrown laundry_wq = create_singlethread_workqueue("nfsd4"); 415529ab23ccSJ. Bruce Fields if (laundry_wq == NULL) 415629ab23ccSJ. Bruce Fields return -ENOMEM; 4157b5a1a81eSJ. Bruce Fields ret = nfsd4_create_callback_queue(); 4158b5a1a81eSJ. Bruce Fields if (ret) 4159b5a1a81eSJ. Bruce Fields goto out_free_laundry; 4160e46b498cSJ. Bruce Fields queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); 4161c2f1a551SMeelap Shah set_max_delegations(); 4162b5a1a81eSJ. Bruce Fields return 0; 4163b5a1a81eSJ. Bruce Fields out_free_laundry: 4164b5a1a81eSJ. Bruce Fields destroy_workqueue(laundry_wq); 4165b5a1a81eSJ. Bruce Fields return ret; 41661da177e4SLinus Torvalds } 41671da177e4SLinus Torvalds 416829ab23ccSJ. Bruce Fields int 416976a3550eSNeilBrown nfs4_state_start(void) 41701da177e4SLinus Torvalds { 4171190e4fbfSNeilBrown nfsd4_load_reboot_recovery_data(); 41724ad9a344SJeff Layton return __nfs4_state_start(); 41731da177e4SLinus Torvalds } 41741da177e4SLinus Torvalds 41751da177e4SLinus Torvalds static void 41761da177e4SLinus Torvalds __nfs4_state_shutdown(void) 41771da177e4SLinus Torvalds { 41781da177e4SLinus Torvalds int i; 41791da177e4SLinus Torvalds struct nfs4_client *clp = NULL; 41801da177e4SLinus Torvalds struct nfs4_delegation *dp = NULL; 41811da177e4SLinus Torvalds struct list_head *pos, *next, reaplist; 41821da177e4SLinus Torvalds 41831da177e4SLinus Torvalds for (i = 0; i < CLIENT_HASH_SIZE; i++) { 41841da177e4SLinus Torvalds while (!list_empty(&conf_id_hashtbl[i])) { 41851da177e4SLinus Torvalds clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); 41861da177e4SLinus Torvalds expire_client(clp); 41871da177e4SLinus Torvalds } 41881da177e4SLinus Torvalds while (!list_empty(&unconf_str_hashtbl[i])) { 41891da177e4SLinus Torvalds clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); 41901da177e4SLinus Torvalds expire_client(clp); 41911da177e4SLinus Torvalds } 41921da177e4SLinus Torvalds } 41931da177e4SLinus Torvalds INIT_LIST_HEAD(&reaplist); 41941da177e4SLinus Torvalds spin_lock(&recall_lock); 41951da177e4SLinus Torvalds list_for_each_safe(pos, next, &del_recall_lru) { 41961da177e4SLinus Torvalds dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); 41971da177e4SLinus Torvalds list_move(&dp->dl_recall_lru, &reaplist); 41981da177e4SLinus Torvalds } 41991da177e4SLinus Torvalds spin_unlock(&recall_lock); 42001da177e4SLinus Torvalds list_for_each_safe(pos, next, &reaplist) { 42011da177e4SLinus Torvalds dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); 42021da177e4SLinus Torvalds list_del_init(&dp->dl_recall_lru); 42031da177e4SLinus Torvalds unhash_delegation(dp); 42041da177e4SLinus Torvalds } 42051da177e4SLinus Torvalds 4206190e4fbfSNeilBrown nfsd4_shutdown_recdir(); 42071da177e4SLinus Torvalds } 42081da177e4SLinus Torvalds 42091da177e4SLinus Torvalds void 42101da177e4SLinus Torvalds nfs4_state_shutdown(void) 42111da177e4SLinus Torvalds { 42125e8d5c29SNeilBrown cancel_rearming_delayed_workqueue(laundry_wq, &laundromat_work); 42135e8d5c29SNeilBrown destroy_workqueue(laundry_wq); 42142c5e7615SJ. Bruce Fields locks_end_grace(&nfsd4_manager); 42151da177e4SLinus Torvalds nfs4_lock_state(); 42161da177e4SLinus Torvalds nfs4_release_reclaim(); 42171da177e4SLinus Torvalds __nfs4_state_shutdown(); 42181da177e4SLinus Torvalds nfs4_unlock_state(); 4219c3935e30SJ. Bruce Fields nfsd4_destroy_callback_queue(); 42201da177e4SLinus Torvalds } 42211da177e4SLinus Torvalds 42223dd98a3bSJeff Layton /* 42233dd98a3bSJeff Layton * user_recovery_dirname is protected by the nfsd_mutex since it's only 42243dd98a3bSJeff Layton * accessed when nfsd is starting. 42253dd98a3bSJeff Layton */ 42260964a3d3SNeilBrown static void 42270964a3d3SNeilBrown nfs4_set_recdir(char *recdir) 42280964a3d3SNeilBrown { 42290964a3d3SNeilBrown strcpy(user_recovery_dirname, recdir); 42300964a3d3SNeilBrown } 42310964a3d3SNeilBrown 42320964a3d3SNeilBrown /* 42330964a3d3SNeilBrown * Change the NFSv4 recovery directory to recdir. 42340964a3d3SNeilBrown */ 42350964a3d3SNeilBrown int 42360964a3d3SNeilBrown nfs4_reset_recoverydir(char *recdir) 42370964a3d3SNeilBrown { 42380964a3d3SNeilBrown int status; 4239a63bb996SAl Viro struct path path; 42400964a3d3SNeilBrown 4241a63bb996SAl Viro status = kern_path(recdir, LOOKUP_FOLLOW, &path); 42420964a3d3SNeilBrown if (status) 42430964a3d3SNeilBrown return status; 42440964a3d3SNeilBrown status = -ENOTDIR; 4245a63bb996SAl Viro if (S_ISDIR(path.dentry->d_inode->i_mode)) { 42460964a3d3SNeilBrown nfs4_set_recdir(recdir); 42470964a3d3SNeilBrown status = 0; 42480964a3d3SNeilBrown } 4249a63bb996SAl Viro path_put(&path); 42500964a3d3SNeilBrown return status; 42510964a3d3SNeilBrown } 42520964a3d3SNeilBrown 42533dd98a3bSJeff Layton char * 42543dd98a3bSJeff Layton nfs4_recoverydir(void) 42553dd98a3bSJeff Layton { 42563dd98a3bSJeff Layton return user_recovery_dirname; 42573dd98a3bSJeff Layton } 4258