📄 nfs4state.c
字号:
fp = kmem_cache_alloc(file_slab, GFP_KERNEL); if (fp) { kref_init(&fp->fi_ref); INIT_LIST_HEAD(&fp->fi_hash); INIT_LIST_HEAD(&fp->fi_stateids); INIT_LIST_HEAD(&fp->fi_delegations); list_add(&fp->fi_hash, &file_hashtbl[hashval]); fp->fi_inode = igrab(ino); fp->fi_id = current_fileid++; fp->fi_had_conflict = false; return fp; } return NULL;}static voidnfsd4_free_slab(struct kmem_cache **slab){ if (*slab == NULL) return; kmem_cache_destroy(*slab); *slab = NULL;}voidnfsd4_free_slabs(void){ nfsd4_free_slab(&stateowner_slab); nfsd4_free_slab(&file_slab); nfsd4_free_slab(&stateid_slab); nfsd4_free_slab(&deleg_slab);}static intnfsd4_init_slabs(void){ stateowner_slab = kmem_cache_create("nfsd4_stateowners", sizeof(struct nfs4_stateowner), 0, 0, NULL); if (stateowner_slab == NULL) goto out_nomem; file_slab = kmem_cache_create("nfsd4_files", sizeof(struct nfs4_file), 0, 0, NULL); if (file_slab == NULL) goto out_nomem; stateid_slab = kmem_cache_create("nfsd4_stateids", sizeof(struct nfs4_stateid), 0, 0, NULL); if (stateid_slab == NULL) goto out_nomem; deleg_slab = kmem_cache_create("nfsd4_delegations", sizeof(struct nfs4_delegation), 0, 0, NULL); if (deleg_slab == NULL) goto out_nomem; return 0;out_nomem: nfsd4_free_slabs(); dprintk("nfsd4: out of memory while initializing nfsv4\n"); return -ENOMEM;}voidnfs4_free_stateowner(struct kref *kref){ struct nfs4_stateowner *sop = container_of(kref, struct nfs4_stateowner, so_ref); kfree(sop->so_owner.data); kmem_cache_free(stateowner_slab, sop);}static inline struct nfs4_stateowner *alloc_stateowner(struct xdr_netobj *owner){ struct nfs4_stateowner *sop; if ((sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL))) { if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { memcpy(sop->so_owner.data, owner->data, owner->len); sop->so_owner.len = owner->len; kref_init(&sop->so_ref); return sop; } kmem_cache_free(stateowner_slab, sop); } return NULL;}static struct nfs4_stateowner *alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) { struct nfs4_stateowner *sop; struct nfs4_replay *rp; unsigned int idhashval; if (!(sop = alloc_stateowner(&open->op_owner))) return NULL; idhashval = ownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_stateids); INIT_LIST_HEAD(&sop->so_perstateid); /* not used */ INIT_LIST_HEAD(&sop->so_close_lru); sop->so_time = 0; list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); list_add(&sop->so_perclient, &clp->cl_openowners); sop->so_is_open_owner = 1; sop->so_id = current_ownerid++; sop->so_client = clp; sop->so_seqid = open->op_seqid; sop->so_confirmed = 0; rp = &sop->so_replay; rp->rp_status = nfserr_serverfault; rp->rp_buflen = 0; rp->rp_buf = rp->rp_ibuf; return sop;}static voidrelease_stateid_lockowners(struct nfs4_stateid *open_stp){ struct nfs4_stateowner *lock_sop; while (!list_empty(&open_stp->st_lockowners)) { lock_sop = list_entry(open_stp->st_lockowners.next, struct nfs4_stateowner, so_perstateid); /* list_del(&open_stp->st_lockowners); */ BUG_ON(lock_sop->so_is_open_owner); release_stateowner(lock_sop); }}static voidunhash_stateowner(struct nfs4_stateowner *sop){ struct nfs4_stateid *stp; list_del(&sop->so_idhash); list_del(&sop->so_strhash); if (sop->so_is_open_owner) list_del(&sop->so_perclient); list_del(&sop->so_perstateid); while (!list_empty(&sop->so_stateids)) { stp = list_entry(sop->so_stateids.next, struct nfs4_stateid, st_perstateowner); if (sop->so_is_open_owner) release_stateid(stp, OPEN_STATE); else release_stateid(stp, LOCK_STATE); }}static voidrelease_stateowner(struct nfs4_stateowner *sop){ unhash_stateowner(sop); list_del(&sop->so_close_lru); nfs4_put_stateowner(sop);}static inline voidinit_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { struct nfs4_stateowner *sop = open->op_stateowner; unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); INIT_LIST_HEAD(&stp->st_hash); INIT_LIST_HEAD(&stp->st_perstateowner); INIT_LIST_HEAD(&stp->st_lockowners); INIT_LIST_HEAD(&stp->st_perfile); list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perstateowner, &sop->so_stateids); list_add(&stp->st_perfile, &fp->fi_stateids); stp->st_stateowner = sop; get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; stp->st_access_bmap = 0; stp->st_deny_bmap = 0; __set_bit(open->op_share_access, &stp->st_access_bmap); __set_bit(open->op_share_deny, &stp->st_deny_bmap); stp->st_openstp = NULL;}static voidrelease_stateid(struct nfs4_stateid *stp, int flags){ struct file *filp = stp->st_vfs_file; list_del(&stp->st_hash); list_del(&stp->st_perfile); list_del(&stp->st_perstateowner); if (flags & OPEN_STATE) { release_stateid_lockowners(stp); stp->st_vfs_file = NULL; nfsd_close(filp); } else if (flags & LOCK_STATE) locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp);}static voidmove_to_close_lru(struct nfs4_stateowner *sop){ dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); list_move_tail(&sop->so_close_lru, &close_lru); sop->so_time = get_seconds();}static intsame_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, clientid_t *clid){ return (sop->so_owner.len == owner->len) && 0 == memcmp(sop->so_owner.data, owner->data, owner->len) && (sop->so_client->cl_clientid.cl_id == clid->cl_id);}static struct nfs4_stateowner *find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open){ struct nfs4_stateowner *so = NULL; list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { if (same_owner_str(so, &open->op_owner, &open->op_clientid)) return so; } return NULL;}/* search file_hashtbl[] for file */static struct nfs4_file *find_file(struct inode *ino){ unsigned int hashval = file_hashval(ino); struct nfs4_file *fp; list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { if (fp->fi_inode == ino) { get_nfs4_file(fp); return fp; } } return NULL;}static int access_valid(u32 x){ return (x > 0 && x < 4);}static int deny_valid(u32 x){ return (x >= 0 && x < 5);}static voidset_access(unsigned int *access, unsigned long bmap) { int i; *access = 0; for (i = 1; i < 4; i++) { if (test_bit(i, &bmap)) *access |= i; }}static voidset_deny(unsigned int *deny, unsigned long bmap) { int i; *deny = 0; for (i = 0; i < 4; i++) { if (test_bit(i, &bmap)) *deny |= i ; }}static inttest_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { unsigned int access, deny; set_access(&access, stp->st_access_bmap); set_deny(&deny, stp->st_deny_bmap); if ((access & open->op_share_deny) || (deny & open->op_share_access)) return 0; return 1;}/* * Called to check deny when READ with all zero stateid or * WRITE with all zero or all one stateid */static __be32nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type){ struct inode *ino = current_fh->fh_dentry->d_inode; struct nfs4_file *fp; struct nfs4_stateid *stp; __be32 ret; dprintk("NFSD: nfs4_share_conflict\n"); fp = find_file(ino); if (!fp) return nfs_ok; ret = nfserr_locked; /* Search for conflicting share reservations */ list_for_each_entry(stp, &fp->fi_stateids, st_perfile) { if (test_bit(deny_type, &stp->st_deny_bmap) || test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) goto out; } ret = nfs_ok;out: put_nfs4_file(fp); return ret;}static inline voidnfs4_file_downgrade(struct file *filp, unsigned int share_access){ if (share_access & NFS4_SHARE_ACCESS_WRITE) { put_write_access(filp->f_path.dentry->d_inode); filp->f_mode = (filp->f_mode | FMODE_READ) & ~FMODE_WRITE; }}/* * Recall a delegation */static intdo_recall(void *__dp){ struct nfs4_delegation *dp = __dp; dp->dl_file->fi_had_conflict = true; nfsd4_cb_recall(dp); return 0;}/* * Spawn a thread to perform a recall on the delegation represented * by the lease (file_lock) * * Called from break_lease() with lock_kernel() held. * Note: we assume break_lease will only call this *once* for any given * lease. */staticvoid nfsd_break_deleg_cb(struct file_lock *fl){ struct nfs4_delegation *dp= (struct nfs4_delegation *)fl->fl_owner; struct task_struct *t; dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl); if (!dp) return; /* We're assuming the state code never drops its reference * without first removing the lease. Since we're in this lease * callback (and since the lease code is serialized by the kernel * lock) we know the server hasn't removed the lease yet, we know * it's safe to take a reference: */ atomic_inc(&dp->dl_count); atomic_inc(&dp->dl_client->cl_count); spin_lock(&recall_lock); list_add_tail(&dp->dl_recall_lru, &del_recall_lru); spin_unlock(&recall_lock); /* only place dl_time is set. protected by lock_kernel*/ dp->dl_time = get_seconds(); /* * We don't want the locks code to timeout the lease for us; * we'll remove it ourself if the delegation isn't returned * in time. */ fl->fl_break_time = 0; t = kthread_run(do_recall, dp, "%s", "nfs4_cb_recall"); if (IS_ERR(t)) { struct nfs4_client *clp = dp->dl_client; printk(KERN_INFO "NFSD: Callback thread failed for " "for client (clientid %08x/%08x)\n", clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); put_nfs4_client(dp->dl_client); nfs4_put_delegation(dp); }}/* * The file_lock is being reapd. * * Called by locks_free_lock() with lock_kernel() held. */staticvoid nfsd_release_deleg_cb(struct file_lock *fl){ struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; dprintk("NFSD nfsd_release_deleg_cb: fl %p dp %p dl_count %d\n", fl,dp, atomic_read(&dp->dl_count)); if (!(fl->fl_flags & FL_LEASE) || !dp) return; dp->dl_flock = NULL;}/* * Set the delegation file_lock back pointer. * * Called from setlease() with lock_kernel() held. */staticvoid nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl){ struct nfs4_delegation *dp = (struct nfs4_delegation *)new->fl_owner; dprintk("NFSD: nfsd_copy_lock_deleg_cb: new fl %p dp %p\n", new, dp); if (!dp) return; dp->dl_flock = new;}/* * Called from setlease() with lock_kernel() held */staticint nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try){ struct nfs4_delegation *onlistd = (struct nfs4_delegation *)onlist->fl_owner; struct nfs4_delegation *tryd = (struct nfs4_delegation *)try->fl_owner; if (onlist->fl_lmops != try->fl_lmops) return 0; return onlistd->dl_client == tryd->dl_client;}staticint nfsd_change_deleg_cb(struct file_lock **onlist, int arg){ if (arg & F_UNLCK) return lease_modify(onlist, arg); else return -EAGAIN;}static struct lock_manager_operations nfsd_lease_mng_ops = { .fl_break = nfsd_break_deleg_cb, .fl_release_private = nfsd_release_deleg_cb, .fl_copy_lock = nfsd_copy_lock_deleg_cb, .fl_mylease = nfsd_same_client_deleg_cb, .fl_change = nfsd_change_deleg_cb,};__be32nfsd4_process_open1(struct nfsd4_open *open){ clientid_t *clientid = &open->op_clientid; struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_stateowner *sop = NULL; if (!check_name(open->op_owner)) return nfserr_inval; if (STALE_CLIENTID(&open->op_clientid)) return nfserr_stale_clientid; strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner); sop = find_openstateowner_str(strhashval, open); open->op_stateowner = sop; if (!sop) { /* Make sure the client's lease hasn't expired. */ clp = find_confirmed_client(clientid); if (clp == NULL) return nfserr_expired; goto renew; } if (!sop->so_confirmed) { /* Replace unconfirmed owners without checking for replay. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -