Commit ed5dc775 authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] knfsd: nfsd byte range locking - LOCK

From: "William A.(Andy) Adamson" <andros@citi.umich.edu>

This implements the nfsv4 LOCK operation.
parent 902773e5
......@@ -377,7 +377,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read
}
/* check stateid */
if ((status = nfs4_preprocess_stateid_op(current_fh, &read->rd_stateid,
CHECK_FH, &stp))) {
CHECK_FH | RDWR_STATE, &stp))) {
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out;
}
......@@ -467,7 +467,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_se
nfs4_lock_state();
if ((status = nfs4_preprocess_stateid_op(current_fh,
&setattr->sa_stateid,
CHECK_FH, &stp))) {
CHECK_FH | RDWR_STATE, &stp))) {
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
goto out;
}
......@@ -507,7 +507,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
goto zero_stateid;
}
if ((status = nfs4_preprocess_stateid_op(current_fh, stateid,
CHECK_FH, &stp))) {
CHECK_FH | RDWR_STATE, &stp))) {
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
goto out;
}
......@@ -681,6 +681,9 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
case OP_LINK:
op->status = nfsd4_link(rqstp, &current_fh, &save_fh, &op->u.link);
break;
case OP_LOCK:
op->status = nfsd4_lock(rqstp, &current_fh, &op->u.lock);
break;
case OP_LOOKUP:
op->status = nfsd4_lookup(rqstp, &current_fh, &op->u.lookup);
break;
......
......@@ -38,7 +38,6 @@
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h>
......@@ -70,6 +69,10 @@ u32 alloc_sowner = 0;
u32 free_sowner = 0;
u32 vfsopen = 0;
u32 vfsclose = 0;
u32 alloc_lsowner= 0;
/* forward declarations */
struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
/* Locking:
*
......@@ -106,7 +109,7 @@ opaque_hashval(const void *ptr, int nbytes)
/* forward declarations */
static void release_stateowner(struct nfs4_stateowner *sop);
static void release_stateid(struct nfs4_stateid *stp);
static void release_stateid(struct nfs4_stateid *stp, int flags);
static void release_file(struct nfs4_file *fp);
......@@ -757,7 +760,7 @@ free_stateowner(struct nfs4_stateowner *sop) {
}
static struct nfs4_stateowner *
alloc_init_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) {
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;
......@@ -773,6 +776,7 @@ alloc_init_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct n
list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]);
list_add(&sop->so_perclient, &clp->cl_perclient);
add_perclient++;
sop->so_is_open_owner = 1;
sop->so_id = current_ownerid++;
sop->so_client = clp;
sop->so_seqid = open->op_seqid;
......@@ -797,7 +801,10 @@ release_stateowner(struct nfs4_stateowner *sop)
while (!list_empty(&sop->so_perfilestate)) {
stp = list_entry(sop->so_perfilestate.next,
struct nfs4_stateid, st_perfilestate);
release_stateid(stp);
if(sop->so_is_open_owner)
release_stateid(stp, OPEN_STATE);
else
release_stateid(stp, LOCK_STATE);
}
free_stateowner(sop);
}
......@@ -824,13 +831,13 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfs4_stateow
}
static void
release_stateid(struct nfs4_stateid *stp) {
release_stateid(struct nfs4_stateid *stp, int flags) {
list_del_init(&stp->st_hash);
list_del_perfile++;
list_del_init(&stp->st_perfile);
list_del_init(&stp->st_perfilestate);
if(stp->st_vfs_set) {
if((stp->st_vfs_set) && (flags & OPEN_STATE)) {
nfsd_close(&stp->st_vfs_file);
vfsclose++;
dput(stp->st_vfs_file.f_dentry);
......@@ -851,13 +858,14 @@ release_file(struct nfs4_file *fp)
}
void
release_open_state(struct nfs4_stateid *stp, struct nfsd4_close *cl)
release_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_open_state\n");
release_stateid(stp);
dprintk("NFSD: release_state_owner\n");
release_stateid(stp, flag);
/*
* release unused nfs4_stateowners.
* XXX will need to be placed on an open_stateid_lru list to be
......@@ -866,7 +874,7 @@ release_open_state(struct nfs4_stateid *stp, struct nfsd4_close *cl)
*/
if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) {
release_stateowner(sop);
cl->cl_stateowner = NULL;
*sopp = NULL;
}
/* unused nfs4_file's are releseed. XXX slab cache? */
if (list_empty(&fp->fi_perfile)) {
......@@ -875,10 +883,10 @@ release_open_state(struct nfs4_stateid *stp, struct nfsd4_close *cl)
}
static int
cmp_owner_str(struct nfs4_stateowner *sop, struct nfsd4_open *open) {
return ((sop->so_owner.len == open->op_owner.len) &&
!memcmp(sop->so_owner.data, open->op_owner.data, sop->so_owner.len) &&
(sop->so_client->cl_clientid.cl_id == open->op_clientid.cl_id));
cmp_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 */
......@@ -889,7 +897,7 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nf
list_for_each_safe(pos, next, &ownerstr_hashtbl[hashval]) {
local = list_entry(pos, struct nfs4_stateowner, so_strhash);
if(!cmp_owner_str(local, open))
if(!cmp_owner_str(local, &open->op_owner, &open->op_clientid))
continue;
*op = local;
return(1);
......@@ -1071,7 +1079,7 @@ nfsd4_process_open1(struct nfsd4_open *open)
goto out;
instantiate_new_owner:
status = nfserr_resource;
if (!(sop = alloc_init_stateowner(strhashval, clp, open)))
if (!(sop = alloc_init_open_stateowner(strhashval, clp, open)))
goto out;
open->op_stateowner = sop;
status = nfs_ok;
......@@ -1205,7 +1213,7 @@ nfsd4_renew(clientid_t *clid)
int status;
nfs4_lock_state();
printk("process_renew(%08x/%08x): starting\n",
dprintk("process_renew(%08x/%08x): starting\n",
clid->cl_boot, clid->cl_id);
status = nfserr_stale_clientid;
if (STALE_CLIENTID(clid))
......@@ -1231,7 +1239,7 @@ nfsd4_renew(clientid_t *clid)
* Presumably this is because the client took too long to
* RENEW, so return NFS4ERR_EXPIRED.
*/
printk("nfsd4_renew: clientid not found!\n");
dprintk("nfsd4_renew: clientid not found!\n");
status = nfserr_expired;
out:
nfs4_unlock_state();
......@@ -1277,25 +1285,6 @@ laundromat_main(void *not_used)
schedule_delayed_work(&laundromat_work, t*HZ);
}
/* search stateid_hashtbl[] for stateid */
struct nfs4_stateid *
find_stateid(stateid_t *stid)
{
struct list_head *pos, *next;
struct nfs4_stateid *local = NULL;
u32 st_id = stid->si_stateownerid;
u32 f_id = stid->si_fileid;
unsigned int hashval = stateid_hashval(st_id, f_id);
list_for_each_safe(pos, next, &stateid_hashtbl[hashval]) {
local = list_entry(pos, struct nfs4_stateid, st_hash);
if((local->st_stateid.si_stateownerid == st_id) &&
(local->st_stateid.si_fileid == f_id))
return local;
}
return NULL;
}
/* search ownerid_hashtbl[] for stateid owner (stateid->si_stateownerid) */
struct nfs4_stateowner *
find_openstateowner_id(u32 st_id) {
......@@ -1338,7 +1327,7 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
struct nfs4_stateid *stp;
int status;
dprintk("NFSD: preprocess_stateid_op:stateid = (%08x/%08x/%08x/%08x)\n",
dprintk("NFSD: preprocess_stateid_op: stateid = (%08x/%08x/%08x/%08x)\n",
stateid->si_boot, stateid->si_stateownerid,
stateid->si_fileid, stateid->si_generation);
......@@ -1351,27 +1340,27 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
/* BAD STATEID */
status = nfserr_bad_stateid;
if (!(stp = find_stateid(stateid))) {
dprintk("NFSD: process stateid: no open stateid!\n");
if (!(stp = find_stateid(stateid, flags))) {
dprintk("NFSD: preprocess_stateid_op: no open stateid!\n");
goto out;
}
if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) {
dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n");
dprintk("NFSD: preprocess_stateid_op: fh-stateid mismatch!\n");
goto out;
}
if (!stp->st_stateowner->so_confirmed) {
dprintk("process_stateid: lockowner not confirmed yet!\n");
dprintk("preprocess_stateid_op: lockowner not confirmed yet!\n");
goto out;
}
if (stateid->si_generation > stp->st_stateid.si_generation) {
dprintk("process_stateid: future stateid?!\n");
dprintk("preprocess_stateid_op: future stateid?!\n");
goto out;
}
/* OLD STATEID */
status = nfserr_old_stateid;
if (stateid->si_generation < stp->st_stateid.si_generation) {
dprintk("process_stateid: old stateid!\n");
dprintk("preprocess_stateid_op: old stateid!\n");
goto out;
}
*stpp = stp;
......@@ -1418,7 +1407,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
* this might be a retransmitted CLOSE which has arrived after
* the openfile has been released.
*/
if (!(stp = find_stateid(stateid)))
if (!(stp = find_stateid(stateid, flags)))
goto no_nfs4_stateid;
status = nfserr_bad_stateid;
......@@ -1507,7 +1496,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
if ((status = nfs4_preprocess_seqid_op(current_fh, oc->oc_seqid,
&oc->oc_req_stateid,
CHECK_FH | CONFIRM,
CHECK_FH | CONFIRM | OPEN_STATE,
&oc->oc_stateowner, &stp)))
goto out;
......@@ -1539,7 +1528,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct n
nfs4_lock_state();
if ((status = nfs4_preprocess_seqid_op(current_fh, od->od_seqid,
&od->od_stateid,
CHECK_FH, &od->od_stateowner, &stp)))
CHECK_FH | OPEN_STATE,
&od->od_stateowner, &stp)))
goto out;
status = nfserr_inval;
......@@ -1578,7 +1568,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_clos
nfs4_lock_state();
if ((status = nfs4_preprocess_seqid_op(current_fh, close->cl_seqid,
&close->cl_stateid,
CHECK_FH,
CHECK_FH | OPEN_STATE,
&close->cl_stateowner, &stp)))
goto out;
/*
......@@ -1588,13 +1578,374 @@ nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_clos
update_stateid(&stp->st_stateid);
memcpy(&close->cl_stateid, &stp->st_stateid, sizeof(stateid_t));
/* release_open_state() calls nfsd_close() if needed */
release_open_state(stp,close);
/* release_state_owner() calls nfsd_close() if needed */
release_state_owner(stp, &close->cl_stateowner, OPEN_STATE);
out:
nfs4_unlock_state();
return status;
}
/*
* Lock owner state (byte-range locks)
*/
#define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start))
#define LOCK_HASH_BITS 8
#define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS)
#define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1)
#define lockownerid_hashval(id) \
((id) & LOCK_HASH_MASK)
#define lock_ownerstr_hashval(x, clientid, ownername) \
((file_hashval(x) + (clientid) + opaque_hashval((ownername.data), (ownername.len))) & LOCK_HASH_MASK)
static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE];
static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE];
static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE];
struct nfs4_stateid *
find_stateid(stateid_t *stid, int flags)
{
struct list_head *pos, *next;
struct nfs4_stateid *local = NULL;
u32 st_id = stid->si_stateownerid;
u32 f_id = stid->si_fileid;
unsigned int hashval;
dprintk("NFSD: find_stateid flags 0x%x\n",flags);
if ((flags & LOCK_STATE) || (flags & RDWR_STATE)) {
hashval = stateid_hashval(st_id, f_id);
list_for_each_safe(pos, next, &lockstateid_hashtbl[hashval]) {
local = list_entry(pos, struct nfs4_stateid, st_hash);
if((local->st_stateid.si_stateownerid == st_id) &&
(local->st_stateid.si_fileid == f_id))
return local;
}
}
if ((flags & OPEN_STATE) || (flags & RDWR_STATE)) {
hashval = stateid_hashval(st_id, f_id);
list_for_each_safe(pos, next, &stateid_hashtbl[hashval]) {
local = list_entry(pos, struct nfs4_stateid, st_hash);
if((local->st_stateid.si_stateownerid == st_id) &&
(local->st_stateid.si_fileid == f_id))
return local;
}
} else
printk("NFSD: find_stateid: ERROR: no state flag\n");
return NULL;
}
/*
* TODO: Linux file offsets are _signed_ 64-bit quantities, which means that
* we can't properly handle lock requests that go beyond the (2^63 - 1)-th
* byte, because of sign extension problems. Since NFSv4 calls for 64-bit
* locking, this prevents us from being completely protocol-compliant. The
* real solution to this problem is to start using unsigned file offsets in
* the VFS, but this is a very deep change!
*/
static inline void
nfs4_transform_lock_offset(struct file_lock *lock)
{
if (lock->fl_start < 0)
lock->fl_start = OFFSET_MAX;
if (lock->fl_end < 0)
lock->fl_end = OFFSET_MAX;
}
int
nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval)
{
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL;
int status = 0;
if (hashval >= LOCK_HASH_SIZE)
goto out;
list_for_each_safe(pos, next, &lock_ownerid_hashtbl[hashval]) {
local = list_entry(pos, struct nfs4_stateowner, so_idhash);
if (local == sop) {
status = 1;
goto out;
}
}
out:
return status;
}
static inline void
nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny)
{
struct nfs4_stateowner *sop = (struct nfs4_stateowner *) fl->fl_owner;
deny->ld_sop = NULL;
if (nfs4_verify_lock_stateowner(sop, fl->fl_pid))
deny->ld_sop = sop;
deny->ld_start = fl->fl_start;
deny->ld_length = ~(u64)0;
if (fl->fl_end != ~(u64)0)
deny->ld_length = fl->fl_end - fl->fl_start + 1;
deny->ld_type = NFS4_READ_LT;
if (fl->fl_type != F_RDLCK)
deny->ld_type = NFS4_WRITE_LT;
}
static int
find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) {
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL;
list_for_each_safe(pos, next, &lock_ownerstr_hashtbl[hashval]) {
local = list_entry(pos, struct nfs4_stateowner, so_strhash);
if(!cmp_owner_str(local, owner, clid))
continue;
*op = local;
return(1);
}
*op = NULL;
return 0;
}
/*
* Alloc a lock owner structure.
* Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has
* occured.
*
* strhashval = lock_ownerstr_hashval
* so_seqid = lock->lk_new_lock_seqid - 1: it gets bumped in encode
*/
static struct nfs4_stateowner *
alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_lock *lock) {
struct nfs4_stateowner *sop;
struct nfs4_replay *rp;
unsigned int idhashval;
if (!(sop = alloc_stateowner(&lock->lk_new_owner)))
return (struct nfs4_stateowner *)NULL;
idhashval = lockownerid_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_perfilestate);
list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]);
list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]);
list_add(&sop->so_perclient, &clp->cl_perclient);
add_perclient++;
sop->so_is_open_owner = 0;
sop->so_id = current_ownerid++;
sop->so_client = clp;
sop->so_seqid = lock->lk_new_lock_seqid - 1;
sop->so_confirmed = 1;
rp = &sop->so_replay;
rp->rp_status = NFSERR_SERVERFAULT;
rp->rp_buflen = 0;
rp->rp_buf = rp->rp_ibuf;
alloc_lsowner++;
return sop;
}
struct nfs4_stateid *
alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp)
{
struct nfs4_stateid *stp;
unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id);
if ((stp = kmalloc(sizeof(struct nfs4_stateid),
GFP_KERNEL)) == NULL)
goto out;
INIT_LIST_HEAD(&stp->st_hash);
INIT_LIST_HEAD(&stp->st_perfile);
INIT_LIST_HEAD(&stp->st_perfilestate);
list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]);
list_add(&stp->st_perfile, &fp->fi_perfile);
list_add_perfile++;
list_add(&stp->st_perfilestate, &sop->so_perfilestate);
stp->st_stateowner = sop;
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_vfs_file = open_stp->st_vfs_file;
stp->st_vfs_set = open_stp->st_vfs_set;
stp->st_share_access = -1;
stp->st_share_deny = -1;
out:
return stp;
}
/*
* LOCK operation
*/
int
nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock)
{
struct nfs4_stateowner *lock_sop = NULL, *open_sop = NULL;
struct nfs4_stateid *lock_stp;
struct file *filp;
struct file_lock file_lock;
struct file_lock *conflock;
int status = 0;
unsigned int strhashval;
dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n",
lock->lk_offset, lock->lk_length);
lock->lk_stateowner = NULL;
nfs4_lock_state();
if (lock->lk_is_new) {
/*
* Client indicates that this is a new lockowner.
* Use open owner and open stateid to create lock owner and lock
* stateid.
*/
struct nfs4_stateid *open_stp = NULL;
struct nfs4_file *fp;
status = nfserr_stale_clientid;
if (STALE_CLIENTID(&lock->lk_new_clientid)) {
printk("NFSD: nfsd4_lock: clientid is stale!\n");
goto out;
}
/* validate and update open stateid and open seqid */
status = nfs4_preprocess_seqid_op(current_fh,
lock->lk_new_open_seqid,
&lock->lk_new_open_stateid,
CHECK_FH | OPEN_STATE,
&open_sop, &open_stp);
if (status)
goto out;
/* create lockowner and lock stateid */
fp = open_stp->st_file;
strhashval = lock_ownerstr_hashval(fp->fi_inode,
open_sop->so_client->cl_clientid.cl_id,
lock->v.new.owner);
/*
* If we already have this lock owner, the client is in
* error (or our bookeeping is wrong!)
* for asking for a 'new lock'.
*/
status = nfserr_bad_stateid;
if (find_lockstateowner_str(strhashval, &lock->v.new.owner,
&lock->v.new.clientid, &lock_sop))
goto out;
status = nfserr_resource;
if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval,
open_sop->so_client, lock)))
goto out;
if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner,
fp, open_stp)) == NULL)
goto out;
/* bump the open seqid used to create the lock */
open_sop->so_seqid++;
} else {
/* lock (lock owner + lock stateid) already exists */
status = nfs4_preprocess_seqid_op(current_fh,
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
CHECK_FH | LOCK_STATE,
&lock->lk_stateowner, &lock_stp);
if (status)
goto out;
}
/* lock->lk_stateowner and lock_stp have been created or found */
filp = &lock_stp->st_vfs_file;
if ((status = fh_verify(rqstp, current_fh, S_IFREG, MAY_LOCK))) {
printk("NFSD: nfsd4_lock: permission denied!\n");
goto out;
}
switch (lock->lk_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
file_lock.fl_type = F_RDLCK;
break;
case NFS4_WRITE_LT:
case NFS4_WRITEW_LT:
file_lock.fl_type = F_WRLCK;
break;
default:
status = nfserr_inval;
goto out;
}
file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner;
file_lock.fl_pid = lockownerid_hashval(lock->lk_stateowner->so_id);
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
file_lock.fl_notify = NULL;
file_lock.fl_insert = NULL;
file_lock.fl_remove = NULL;
file_lock.fl_start = lock->lk_offset;
if ((lock->lk_length == ~(u64)0) ||
LOFF_OVERFLOW(lock->lk_offset, lock->lk_length))
file_lock.fl_end = ~(u64)0;
else
file_lock.fl_end = lock->lk_offset + lock->lk_length - 1;
nfs4_transform_lock_offset(&file_lock);
/*
* Try to lock the file in the VFS.
* Note: locks.c uses the BKL to protect the inode's lock list.
*/
status = posix_lock_file(filp, &file_lock);
dprintk("NFSD: nfsd4_lock: posix_test_lock passed. posix_lock_file status %d\n",status);
switch (-status) {
case 0: /* success! */
update_stateid(&lock_stp->st_stateid);
memcpy(&lock->lk_resp_stateid, &lock_stp->st_stateid,
sizeof(stateid_t));
goto out;
case (EAGAIN):
goto conflicting_lock;
case (EDEADLK):
status = nfserr_deadlock;
default:
dprintk("NFSD: nfsd4_lock: posix_lock_file() failed! status %d\n",status);
goto out_destroy_new_stateid;
}
conflicting_lock:
dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
status = nfserr_denied;
/* XXX There is a race here. Future patch needed to provide
* an atomic posix_lock_and_test_file
*/
if (!(conflock = posix_test_lock(filp, &file_lock))) {
status = nfserr_serverfault;
goto out;
}
nfs4_set_lock_denied(conflock, &lock->lk_denied);
out_destroy_new_stateid:
if (lock->lk_is_new) {
dprintk("NFSD: nfsd4_lock: destroy new stateid!\n");
/*
* An error encountered after instantiation of the new
* stateid has forced us to destroy it.
*/
if (!seqid_mutating_err(status))
open_sop->so_seqid--;
release_state_owner(lock_stp, &lock->lk_stateowner, LOCK_STATE);
}
out:
nfs4_unlock_state();
return status;
}
/*
* Start and stop routines
*/
void
nfs4_state_init(void)
{
......@@ -1617,6 +1968,11 @@ nfs4_state_init(void)
}
for (i = 0; i < STATEID_HASH_SIZE; i++) {
INIT_LIST_HEAD(&stateid_hashtbl[i]);
INIT_LIST_HEAD(&lockstateid_hashtbl[i]);
}
for (i = 0; i < LOCK_HASH_SIZE; i++) {
INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]);
INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]);
}
memset(&zerostateid, 0, sizeof(stateid_t));
memset(&onestateid, ~0, sizeof(stateid_t));
......@@ -1656,8 +2012,8 @@ __nfs4_state_shutdown(void)
add_perclient, del_perclient);
dprintk("NFSD: alloc_file %d free_file %d\n",
alloc_file, free_file);
dprintk("NFSD: alloc_sowner %d free_sowner %d\n",
alloc_sowner, free_sowner);
dprintk("NFSD: alloc_sowner %d alloc_lsowner %d free_sowner %d\n",
alloc_sowner, alloc_lsowner, free_sowner);
dprintk("NFSD: vfsopen %d vfsclose %d\n",
vfsopen, vfsclose);
}
......
......@@ -567,6 +567,44 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
DECODE_TAIL;
}
static int
nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
{
DECODE_HEAD;
/*
* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
*/
READ_BUF(28);
READ32(lock->lk_type);
if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
goto xdr_error;
READ32(lock->lk_reclaim);
READ64(lock->lk_offset);
READ64(lock->lk_length);
READ32(lock->lk_is_new);
if (lock->lk_is_new) {
READ_BUF(36);
READ32(lock->lk_new_open_seqid);
READ32(lock->lk_new_open_stateid.si_generation);
COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t));
READ32(lock->lk_new_lock_seqid);
COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
READ32(lock->lk_new_owner.len);
READ_BUF(lock->lk_new_owner.len);
READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
} else {
READ_BUF(20);
READ32(lock->lk_old_lock_stateid.si_generation);
COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t));
READ32(lock->lk_old_lock_seqid);
}
DECODE_TAIL;
}
static int
nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
{
......@@ -989,6 +1027,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
case OP_LINK:
op->status = nfsd4_decode_link(argp, &op->u.link);
break;
case OP_LOCK:
op->status = nfsd4_decode_lock(argp, &op->u.lock);
break;
case OP_LOOKUP:
op->status = nfsd4_decode_lookup(argp, &op->u.lookup);
break;
......@@ -1709,6 +1750,42 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, int nfserr, struct svc_fh *fh
}
}
/*
* Including all fields other than the name, a LOCK4denied structure requires
* 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
*/
static void
nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
{
ENCODE_HEAD;
RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop->so_owner.len));
WRITE64(ld->ld_start);
WRITE64(ld->ld_length);
WRITE32(ld->ld_type);
WRITEMEM(&ld->ld_sop->so_client->cl_clientid, 8);
WRITE32(ld->ld_sop->so_owner.len);
WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
ADJUST_ARGS();
}
static void
nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock)
{
ENCODE_SEQID_OP_HEAD;
if (!nfserr) {
RESERVE_SPACE(4 + sizeof(stateid_t));
WRITE32(lock->lk_resp_stateid.si_generation);
WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
ADJUST_ARGS();
} else if (nfserr == nfserr_denied)
nfsd4_encode_lock_denied(resp, &lock->lk_denied);
ENCODE_SEQID_OP_TAIL(lock->lk_stateowner);
}
static void
nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link *link)
{
......@@ -2119,6 +2196,9 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
case OP_LINK:
nfsd4_encode_link(resp, op->status, &op->u.link);
break;
case OP_LOCK:
nfsd4_encode_lock(resp, op->status, &op->u.lock);
break;
case OP_LOOKUP:
break;
case OP_LOOKUPP:
......
......@@ -134,6 +134,15 @@ enum open_delegation_type4 {
NFS4_OPEN_DELEGATE_WRITE = 2
};
enum lock_type4 {
NFS4_UNLOCK_LT = 0,
NFS4_READ_LT = 1,
NFS4_WRITE_LT = 2,
NFS4_READW_LT = 3,
NFS4_WRITEW_LT = 4
};
/* Mandatory Attributes */
#define FATTR4_WORD0_SUPPORTED_ATTRS (1)
#define FATTR4_WORD0_TYPE (1 << 1)
......
......@@ -172,6 +172,8 @@ void nfsd_lockd_shutdown(void);
#define nfserr_serverfault __constant_htonl(NFSERR_SERVERFAULT)
#define nfserr_badtype __constant_htonl(NFSERR_BADTYPE)
#define nfserr_jukebox __constant_htonl(NFSERR_JUKEBOX)
#define nfserr_denied __constant_htonl(NFSERR_DENIED)
#define nfserr_deadlock __constant_htonl(NFSERR_DEADLOCK)
#define nfserr_expired __constant_htonl(NFSERR_EXPIRED)
#define nfserr_bad_cookie __constant_htonl(NFSERR_BAD_COOKIE)
#define nfserr_same __constant_htonl(NFSERR_SAME)
......
......@@ -117,16 +117,24 @@ struct nfs4_replay {
};
/*
* nfs4_stateowner can either be an open_owner, or (eventually) a lock_owner
* nfs4_stateowner can either be an open_owner, or a lock_owner
*
* o so_perfilestate list is used to ensure no dangling nfs4_stateid
* reverences when we release a stateowner.
* so_idhash: stateid_hashtbl[] for open owner, lockstateid_hashtbl[]
* for lock_owner
* so_strhash: ownerstr_hashtbl[] for open_owner, lock_ownerstr_hashtbl[]
* for lock_owner
* so_perclient: nfs4_client->cl_perclient entry - used when nfs4_client
* struct is reaped.
* so_perfilestate: heads the list of nfs4_stateid (either open or lock)
* and is used to ensure no dangling nfs4_stateid references when we
* release a stateowner.
*/
struct nfs4_stateowner {
struct list_head so_idhash; /* hash by so_id */
struct list_head so_strhash; /* hash by op_name */
struct list_head so_perclient; /* nfs4_client->cl_perclient */
struct list_head so_perfilestate; /* list: nfs4_stateid */
int so_is_open_owner; /* 1=openowner,0=lockowner */
u32 so_id;
struct nfs4_client * so_client;
u32 so_seqid;
......@@ -152,6 +160,12 @@ struct nfs4_file {
* nfs4_stateid can either be an open stateid or (eventually) a lock stateid
*
* (open)nfs4_stateid: one per (open)nfs4_stateowner, nfs4_file
*
* st_hash: stateid_hashtbl[] entry or lockstateid_hashtbl entry
* st_perfile: file_hashtbl[] entry.
* st_perfile_state: nfs4_stateowner->so_perfilestate
* st_share_access: used only for open stateid
* st_share_deny: used only for open stateid
*/
struct nfs4_stateid {
......@@ -170,6 +184,9 @@ struct nfs4_stateid {
/* flags for preprocess_seqid_op() */
#define CHECK_FH 0x00000001
#define CONFIRM 0x00000002
#define OPEN_STATE 0x00000004
#define LOCK_STATE 0x00000008
#define RDWR_STATE 0x00000010
#define seqid_mutating_err(err) \
(((err) != nfserr_stale_clientid) && \
......
......@@ -40,6 +40,7 @@
#define _LINUX_NFSD_XDR4_H
#define NFSD4_MAX_TAGLEN 128
#define XDR_LEN(n) (((n) + 3) & ~3)
typedef u32 delegation_zero_t;
typedef u32 delegation_boot_t;
......@@ -111,6 +112,56 @@ struct nfsd4_link {
struct nfsd4_change_info li_cinfo; /* response */
};
struct nfsd4_lock_denied {
struct nfs4_stateowner *ld_sop;
u64 ld_start;
u64 ld_length;
u32 ld_type;
};
struct nfsd4_lock {
/* request */
u32 lk_type;
u32 lk_reclaim; /* boolean */
u64 lk_offset;
u64 lk_length;
u32 lk_is_new;
union {
struct {
u32 open_seqid;
stateid_t open_stateid;
u32 lock_seqid;
clientid_t clientid;
struct xdr_netobj owner;
} new;
struct {
stateid_t lock_stateid;
u32 lock_seqid;
} old;
} v;
/* response */
union {
struct {
stateid_t stateid;
} ok;
struct nfsd4_lock_denied denied;
} u;
struct nfs4_stateowner *lk_stateowner;
};
#define lk_new_open_seqid v.new.open_seqid
#define lk_new_open_stateid v.new.open_stateid
#define lk_new_lock_seqid v.new.lock_seqid
#define lk_new_clientid v.new.clientid
#define lk_new_owner v.new.owner
#define lk_old_lock_stateid v.old.lock_stateid
#define lk_old_lock_seqid v.old.lock_seqid
#define lk_rflags u.ok.rflags
#define lk_resp_stateid u.ok.stateid
#define lk_denied u.denied
struct nfsd4_lookup {
u32 lo_len; /* request */
char * lo_name; /* request */
......@@ -266,6 +317,7 @@ struct nfsd4_op {
struct nfsd4_getattr getattr;
struct svc_fh * getfh;
struct nfsd4_link link;
struct nfsd4_lock lock;
struct nfsd4_lookup lookup;
struct nfsd4_verify nverify;
struct nfsd4_open open;
......@@ -357,6 +409,8 @@ extern int nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh,
struct nfsd4_close *close);
extern int nfsd4_open_downgrade(struct svc_rqst *rqstp,
struct svc_fh *current_fh, struct nfsd4_open_downgrade *od);
extern int nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh,
struct nfsd4_lock *lock);
#endif
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment