nfs4state.c
字号:
nfsd_close(stp->st_vfs_file); vfsclose++; } else if ((stp->st_vfs_set) && (flags & LOCK_STATE)) { struct file *filp = stp->st_vfs_file; locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); } kfree(stp); stp = NULL;}static voidrelease_file(struct nfs4_file *fp){ free_file++; list_del(&fp->fi_hash); iput(fp->fi_inode); kfree(fp);} voidmove_to_close_lru(struct nfs4_stateowner *sop){ dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); unhash_stateowner(sop); list_add_tail(&sop->so_close_lru, &close_lru); sop->so_time = get_seconds();}voidrelease_state_owner(struct nfs4_stateid *stp, struct nfs4_stateowner **sopp, int flag){ struct nfs4_stateowner *sop = stp->st_stateowner; struct nfs4_file *fp = stp->st_file; dprintk("NFSD: release_state_owner\n"); release_stateid(stp, flag); /* place unused nfs4_stateowners on so_close_lru list to be * released by the laundromat service after the lease period * to enable us to handle CLOSE replay */ if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) move_to_close_lru(sop); /* unused nfs4_file's are releseed. XXX slab cache? */ if (list_empty(&fp->fi_perfile)) { release_file(fp); }}static intcmp_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, clientid_t *clid) { return ((sop->so_owner.len == owner->len) && !memcmp(sop->so_owner.data, owner->data, owner->len) && (sop->so_client->cl_clientid.cl_id == clid->cl_id));}/* search ownerstr_hashtbl[] for owner */static intfind_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nfs4_stateowner **op) { struct nfs4_stateowner *local = NULL; list_for_each_entry(local, &ownerstr_hashtbl[hashval], so_strhash) { if (!cmp_owner_str(local, &open->op_owner, &open->op_clientid)) continue; *op = local; return(1); } return 0;}/* see if clientid is in confirmed hash table */static intverify_clientid(struct nfs4_client **client, clientid_t *clid) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { if (!cmp_clid(&clp->cl_clientid, clid)) continue; *client = clp; return 1; } *client = NULL; return 0;}/* search file_hashtbl[] for file */static intfind_file(unsigned int hashval, struct inode *ino, struct nfs4_file **fp) { struct nfs4_file *local = NULL; list_for_each_entry(local, &file_hashtbl[hashval], fi_hash) { if (local->fi_inode == ino) { *fp = local; return(1); } } return 0;}#define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0)#define TEST_DENY(x) ((x >= 0 || x < 5)?1:0)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; }}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 */intnfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type){ struct inode *ino = current_fh->fh_dentry->d_inode; unsigned int fi_hashval; struct nfs4_file *fp; struct nfs4_stateid *stp; dprintk("NFSD: nfs4_share_conflict\n"); fi_hashval = file_hashval(ino); if (find_file(fi_hashval, ino, &fp)) { /* Search for conflicting share reservations */ list_for_each_entry(stp, &fp->fi_perfile, st_perfile) { if (test_bit(deny_type, &stp->st_deny_bmap) || test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) return nfserr_share_denied; } } return nfs_ok;}static inline intnfs4_file_upgrade(struct file *filp, unsigned int share_access){int status; if (share_access & NFS4_SHARE_ACCESS_WRITE) { status = get_write_access(filp->f_dentry->d_inode); if (status) return nfserrno(status); filp->f_mode = (filp->f_mode | FMODE_WRITE) & ~FMODE_READ; } return nfs_ok;}static inline voidnfs4_file_downgrade(struct file *filp, unsigned int share_access){ if (share_access & NFS4_SHARE_ACCESS_WRITE) { put_write_access(filp->f_dentry->d_inode); filp->f_mode = (filp->f_mode | FMODE_READ) & ~FMODE_WRITE; }}/* * nfsd4_process_open1() * lookup stateowner. * found: * check confirmed * confirmed: * check seqid * not confirmed: * delete owner * create new owner * notfound: * verify clientid * create new owner * * called with nfs4_lock_state() held. */intnfsd4_process_open1(struct nfsd4_open *open){ int status; clientid_t *clientid = &open->op_clientid; struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_stateowner *sop = NULL; status = nfserr_inval; if (!check_name(open->op_owner)) goto out; status = nfserr_stale_clientid; if (STALE_CLIENTID(&open->op_clientid)) return status; strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner); if (find_openstateowner_str(strhashval, open, &sop)) { open->op_stateowner = sop; /* check for replay */ if (open->op_seqid == sop->so_seqid){ if (!sop->so_replay.rp_buflen) { /* * The original OPEN failed in so spectacularly that we * don't even have replay data saved! Therefore, we * have no choice but to continue processing * this OPEN; presumably, we'll fail again for the same * reason. */ dprintk("nfsd4_process_open1: replay with no replay cache\n"); status = NFS_OK; goto renew; } /* replay: indicate to calling function */ status = NFSERR_REPLAY_ME; return status; } if (sop->so_confirmed) { if (open->op_seqid == sop->so_seqid + 1) { status = nfs_ok; goto renew; } status = nfserr_bad_seqid; goto out; } /* If we get here, we received and OPEN for an unconfirmed * nfs4_stateowner. * Since the sequid's are different, purge the * existing nfs4_stateowner, and instantiate a new one. */ clp = sop->so_client; release_stateowner(sop); goto instantiate_new_owner; } /* nfs4_stateowner not found. * verify clientid and instantiate new nfs4_stateowner * if verify fails this is presumably the result of the * client's lease expiring. * * XXX compare clp->cl_addr with rqstp addr? */ status = nfserr_expired; if (!verify_clientid(&clp, clientid)) goto out;instantiate_new_owner: status = nfserr_resource; if (!(sop = alloc_init_open_stateowner(strhashval, clp, open))) goto out; open->op_stateowner = sop; status = nfs_ok;renew: renew_client(sop->so_client);out: if (status && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) status = nfserr_reclaim_bad; return status;}/* * called with nfs4_lock_state() held. */intnfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open){ struct iattr iattr; struct nfs4_stateowner *sop = open->op_stateowner; struct nfs4_file *fp = NULL; struct inode *ino; unsigned int fi_hashval; struct nfs4_stateid *stq, *stp = NULL; int status; status = nfserr_resource; if (!sop) return status; ino = current_fh->fh_dentry->d_inode; status = nfserr_inval; if (!TEST_ACCESS(open->op_share_access) || !TEST_DENY(open->op_share_deny)) goto out; fi_hashval = file_hashval(ino); if (find_file(fi_hashval, ino, &fp)) { /* Search for conflicting share reservations */ status = nfserr_share_denied; list_for_each_entry(stq, &fp->fi_perfile, st_perfile) { if (stq->st_stateowner == sop) { stp = stq; continue; } /* ignore lock owners */ if (stq->st_stateowner->so_is_open_owner == 0) continue; if (!test_share(stq,open)) goto out; } } else { /* No nfs4_file found; allocate and init a new one */ status = nfserr_resource; if ((fp = alloc_init_file(fi_hashval, ino)) == NULL) goto out; } if (!stp) { int flags = 0; status = nfserr_resource; if ((stp = kmalloc(sizeof(struct nfs4_stateid), GFP_KERNEL)) == NULL) goto out; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) flags = MAY_WRITE; else flags = MAY_READ; if ((status = nfsd_open(rqstp, current_fh, S_IFREG, flags, &stp->st_vfs_file)) != 0) goto out_free; vfsopen++; init_stateid(stp, fp, sop, open); stp->st_vfs_set = 1; } else { /* This is an upgrade of an existing OPEN. * OR the incoming share with the existing * nfs4_stateid share */ unsigned int share_access; set_access(&share_access, stp->st_access_bmap); share_access = ~share_access; share_access &= open->op_share_access; /* update the struct file */ if ((status = nfs4_file_upgrade(stp->st_vfs_file, share_access))) goto out; /* remember the open */ set_bit(open->op_share_access, &stp->st_access_bmap); set_bit(open->op_share_deny, &stp->st_deny_bmap); /* bump the stateid */ update_stateid(&stp->st_stateid); } dprintk("nfs4_process_open2: stateid=(%08x/%08x/%08x/%08x)\n\n", stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid, stp->st_stateid.si_fileid, stp->st_stateid.si_generation); if (open->op_truncate) { iattr.ia_valid = ATTR_SIZE; iattr.ia_size = 0; status = nfsd_setattr(rqstp, current_fh, &iattr, 0, (time_t)0); if (status) goto out; } memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE; status = nfs_ok;out: if (fp && list_empty(&fp->fi_perfile)) release_file(fp); if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) { if (status) status = nfserr_reclaim_bad; else { /* successful reclaim. so_seqid is decremented because * it will be bumped in encode_open */ open->op_stateowner->so_confirmed = 1; open->op_stateowner->so_seqid--; } } /* * To finish the open response, we just need to set the rflags. */ open->op_rflags = 0; if (!open->op_stateowner->so_confirmed) open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM; return status;out_free: kfree(stp); goto out;}static struct work_struct laundromat_work;static void laundromat_main(void *);static DECLARE_WORK(laundromat_work, laundromat_main, NULL);int nfsd4_renew(clientid_t *clid){ struct nfs4_client *clp; unsigned int idhashval; int status; nfs4_lock_state(); dprintk("process_renew(%08x/%08x): starting\n", clid->cl_boot, clid->cl_id); status = nfserr_stale_clientid; if (STALE_CLIENTID(clid)) goto out; status = nfs_ok; idhashval = clientid_hashval(clid->cl_id); list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { if (!cmp_clid(&clp->cl_clientid, clid)) continue; renew_client(clp); goto out; } list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { if (!cmp_clid(&clp->cl_clientid, clid)) continue; renew_client(clp); goto out; } /* * Couldn't find an nfs4_client for this clientid. * Presumably this is because the client took too long to * RENEW, so return NFS4ERR_EXPIRED. */ dprintk("nfsd4_renew: clientid not found!\n"); status = nfserr_expired;out: nfs4_unlock_state(); return status;}time_tnfs4_laundromat(void){ struct nfs4_client *clp; struct nfs4_stateowner *sop; struct list_head *pos, *next; time_t cutoff = get_seconds() - NFSD_LEASE_TIME; time_t t, clientid_val = NFSD_LEASE_TIME; time_t u, close_val = NFSD_LEASE_TIME; nfs4_lock_state(); dprintk("NFSD: laundromat service - starting, examining clients\n"); list_for_each_safe(pos, next, &client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { t = clp->cl_time - cutoff; if (clientid_val > t) clientid_val = t; break; } dprintk("NFSD: purging unused client (clientid %08x)\n", clp->cl_clientid.cl_id); expire_client(clp); } list_for_each_safe(pos, next, &close_lru) { sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { u = sop->so_time - cutoff;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -