diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/fs/nfs/file.c linux-2.6.0-24-lock/fs/nfs/file.c --- linux-2.6.0-23-atomic_open2/fs/nfs/file.c 2003-11-11 18:12:56.000000000 -0500 +++ linux-2.6.0-24-lock/fs/nfs/file.c 2003-11-12 00:24:53.000000000 -0500 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -278,21 +277,17 @@ if (!inode) return -EINVAL; - /* This will be in a forthcoming patch. */ - if (NFS_PROTO(inode)->version == 4) { - printk(KERN_INFO "NFS: file locking over NFSv4 is not yet supported\n"); - return -EIO; - } - /* No mandatory locks over NFS */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; - /* Fake OK code if mounted without NLM support */ - if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { - if (IS_GETLK(cmd)) - status = LOCK_USE_CLNT; - goto out_ok; + if (NFS_PROTO(inode)->version != 4) { + /* Fake OK code if mounted without NLM support */ + if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { + if (IS_GETLK(cmd)) + status = LOCK_USE_CLNT; + goto out_ok; + } } /* @@ -322,7 +317,7 @@ return status; lock_kernel(); - status = nlmclnt_proc(inode, cmd, fl); + status = NFS_PROTO(inode)->lock(filp, cmd, fl); unlock_kernel(); if (status < 0) return status; diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/fs/nfs/nfs3proc.c linux-2.6.0-24-lock/fs/nfs/nfs3proc.c --- linux-2.6.0-23-atomic_open2/fs/nfs/nfs3proc.c 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-24-lock/fs/nfs/nfs3proc.c 2003-11-12 00:24:30.000000000 -0500 @@ -15,6 +15,7 @@ #include #include #include +#include #include #define NFSDBG_FACILITY NFSDBG_PROC @@ -896,6 +897,12 @@ return 1; } +static int +nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) +{ + return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); +} + struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -931,4 +938,5 @@ .file_release = nfs_release, .request_init = nfs3_request_init, .request_compatible = nfs3_request_compatible, + .lock = nfs3_proc_lock, }; diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/fs/nfs/nfs4proc.c linux-2.6.0-24-lock/fs/nfs/nfs4proc.c --- linux-2.6.0-23-atomic_open2/fs/nfs/nfs4proc.c 2003-11-11 18:27:37.000000000 -0500 +++ linux-2.6.0-24-lock/fs/nfs/nfs4proc.c 2003-11-12 00:22:20.000000000 -0500 @@ -54,6 +54,8 @@ #define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name #define OPNUM(cp) cp->ops[cp->req_nops].opnum +#define LOCKT_OWNER_ID (0xbebad) + static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; @@ -1438,9 +1440,12 @@ rpc_restart_call(task); req = nfs_list_entry(data->pages.next); - if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); - else + if (req->wb_state) { + if (nfs4_test_lock_state(req->wb_state)) + memcpy(&data->args.stateid, &req->wb_state->lock_state.ls_stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + } else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); } @@ -1490,9 +1495,12 @@ data->res.eof = 0; data->timestamp = jiffies; - if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); - else + if (req->wb_state) { + if (nfs4_test_lock_state(req->wb_state)) + memcpy(&data->args.stateid, &req->wb_state->lock_state.ls_stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + } else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); /* N.B. Do we need to test? Never called for swapfile inode */ @@ -1530,9 +1538,12 @@ rpc_restart_call(task); req = nfs_list_entry(data->pages.next); - if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); - else + if (req->wb_state) { + if (nfs4_test_lock_state(req->wb_state)) + memcpy(&data->args.stateid, &req->wb_state->lock_state.ls_stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + } else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); } @@ -1587,9 +1598,12 @@ data->res.verf = &data->verf; data->timestamp = jiffies; - if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); - else + if (req->wb_state) { + if (nfs4_test_lock_state(req->wb_state)) + memcpy(&data->args.stateid, &req->wb_state->lock_state.ls_stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + } else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); /* Set the initial flags for the task. */ @@ -1949,6 +1963,236 @@ return status; } +#define NFS4_LOCK_MINTIMEOUT (1 * HZ) +#define NFS4_LOCK_MAXTIMEOUT (30 * HZ) +#define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) + +/* + * sleep, with exponential backoff, and retry the LOCK operation. + */ +static int +nfs4_set_lock_task_retry(unsigned long *timeout) +{ +int status = 0; + + current->state = TASK_INTERRUPTIBLE; + status = schedule_timeout(*timeout); + status = -ERESTARTSYS; + if (signal_pending(current)) { + dprintk("nfs4_set_lock_task_retry returns %d\n", status); + return status; + } + current->state = TASK_RUNNING; + *timeout <<= 1; + if (*timeout > NFS4_LOCK_MAXTIMEOUT) + *timeout = NFS4_LOCK_MAXTIMEOUT; + dprintk("nfs4_set_lock_task_retry returns 0\n"); + return 0; +} + +static int +nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs4_state_owner *sop; + struct nfs4_state *osp; + struct nfs4_lock_state *lsp; + unsigned long timeout = NFS4_LOCK_MINTIMEOUT; + loff_t start = request->fl_start, end = request->fl_end; + int status = 0; + int is_blocking = 0; + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .offset = start, + .length = (end != OFFSET_MAX) ? (end - start + 1) : ~(u64)0, + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_argp = &arg, + .rpc_resp = &res, + }; + + /* verify open state */ + osp = (struct nfs4_state *)filp->private_data; + BUG_ON(!osp); + + if ((cmd == F_SETLKW) || (cmd == F_SETLKW64)) + is_blocking = 1; + + /* set lock type */ + switch (request->fl_type) { + case F_RDLCK: + arg.type = is_blocking ? NFS4_READW_LT : NFS4_READ_LT; + break; + case F_WRLCK: + arg.type = is_blocking ? NFS4_WRITEW_LT : NFS4_WRITE_LT; + break; + case F_UNLCK: + arg.type = NFS4_WRITE_LT; + break; + } + + if (cmd == F_GETLK) { /* OP_LOCKT */ + struct file_lock *cfl = NULL; + struct nfs_lowner nlo; + u64 start, length; + + /* Send no RPC on local conflict. */ + status = 0; + if ((cfl = posix_test_lock(filp, request)) != NULL) { + request->fl_start = cfl->fl_start; + request->fl_end = cfl->fl_end; + request->fl_type = cfl->fl_type; + goto out_lockt; + } + + msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT]; + nlo.clientid = server->nfs4_state->cl_clientid; + if (nfs4_test_lock_state(osp)) + nlo.id = osp->lock_state.ls_id; + else + nlo.id = LOCKT_OWNER_ID; + arg.u.lockt = &nlo; + status = rpc_call_sync(server->client, &msg, 0); + if (!status) { + request->fl_type = F_UNLCK; + } + else if (status == -NFS4ERR_DENIED) { + start = res.u.denied.offset; + length = res.u.denied.length; + request->fl_start = start; + if (LOFF_OVERFLOW(start, length)) + request->fl_end = ~(u64) 0; + else + request->fl_end = start + length -1; + request->fl_type = F_WRLCK; + if (res.u.denied.type & 1) + request->fl_type = F_RDLCK; + } +out_lockt: + return status; + } else if (request->fl_type == F_UNLCK) { /* OP_LOCKU */ + struct nfs_locku_opargs luargs; + + + lsp = &osp->lock_state; + if (lsp->ls_seqid == 0) { + /* check that lsp really has no locks on this file */ + if(nfs4_test_unlock(inode, request, osp)) { + return -EIO; + } + return 0; + } + + down(&lsp->ls_sema); + msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU]; + luargs.seqid = lsp->ls_seqid; + memcpy(&luargs.stateid, &lsp->ls_stateid, + sizeof(luargs.stateid)); + arg.u.locku = &luargs; + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_lock_seqid(status, lsp); + + if (status == 0) { + memcpy(&lsp->ls_stateid, &res.u.stateid, + sizeof(lsp->ls_stateid)); + nfs4_put_lock_state(inode, request, osp); + } + up(&lsp->ls_sema); + return status; + + } else { /* OP_LOCK */ + struct nfs_lock_opargs largs = { + .new_lock_owner = 0, + }; + + request->fl_u.nfs_fl.lock_state = NULL; + sop = osp->owner; + + /* allocate lock state */ + lsp = nfs4_get_lock_state(osp, &largs.new_lock_owner); + + msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK]; + largs.reclaim = 0; + if (largs.new_lock_owner) { + struct nfs_open_to_lock otl; + +retry_new: + down(&sop->so_sema); + down(&lsp->ls_sema); + otl.open_seqid = sop->so_seqid; + memcpy(&otl.open_stateid, &osp->stateid, sizeof(otl.open_stateid)); + otl.lock_seqid = lsp->ls_seqid; + otl.lock_owner.clientid = server->nfs4_state->cl_clientid; + otl.lock_owner.id = lsp->ls_id; + largs.u.open_lock = &otl; + arg.u.lock = &largs; + status = rpc_call_sync(server->client, &msg, 0); + /* increment sequence id's on success, and + * seqid mutating errors */ + nfs4_increment_seqid(status, sop); /* OPEN */ + nfs4_increment_lock_seqid(status, lsp); /* LOCK */ + + if ((status == -NFS4ERR_DENIED) && is_blocking) { + status = nfs4_set_lock_task_retry(&timeout); + if (!status) { + up(&sop->so_sema); + up(&lsp->ls_sema); + goto retry_new; + } + } + if (status == 0) { + /* save the returned stateid. + * insert lock state into open state list. */ + memcpy(&lsp->ls_stateid, &res.u.stateid, + sizeof(nfs4_stateid)); + } + if (status == -NFS4ERR_DENIED) + status = -EAGAIN; + + request->fl_u.nfs_fl.lock_state = lsp; + up(&lsp->ls_sema); + up(&sop->so_sema); + return status; + } else { + struct nfs_exist_lock el; + + memcpy(&el.stateid, &lsp->ls_stateid, + sizeof(el.stateid)); +retry: + down(&lsp->ls_sema); + el.seqid = lsp->ls_seqid; + largs.u.exist_lock = ⪙ + arg.u.lock = &largs; + status = rpc_call_sync(server->client, &msg, 0); + /* increment seqi id on success, and + * seqid mutating errors*/ + nfs4_increment_lock_seqid(status, lsp); + + if ((status == -NFS4ERR_DENIED) && is_blocking) { + status = nfs4_set_lock_task_retry(&timeout); + if (!status) { + up(&lsp->ls_sema); + goto retry; + } + } + if (status == 0) { + memcpy(&lsp->ls_stateid, &res.u.stateid, + sizeof(nfs4_stateid)); + } + if (status == -NFS4ERR_DENIED) + status = -EAGAIN; + request->fl_u.nfs_fl.lock_state = lsp; + up(&lsp->ls_sema); + return status; + } + } + return status; +} + struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ .dentry_ops = &nfs4_dentry_operations, @@ -1984,6 +2228,7 @@ .file_release = nfs4_proc_file_release, .request_init = nfs4_request_init, .request_compatible = nfs4_request_compatible, + .lock = nfs4_proc_lock, }; /* diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/fs/nfs/nfs4state.c linux-2.6.0-24-lock/fs/nfs/nfs4state.c --- linux-2.6.0-23-atomic_open2/fs/nfs/nfs4state.c 2003-11-11 18:27:37.000000000 -0500 +++ linux-2.6.0-24-lock/fs/nfs/nfs4state.c 2003-11-11 19:18:01.000000000 -0500 @@ -42,6 +42,7 @@ #include #include #include +#include #define OPENOWNER_POOL_SIZE 8 @@ -265,6 +266,7 @@ nfs4_alloc_open_state(void) { struct nfs4_state *state; + struct nfs4_lock_state *ls; state = kmalloc(sizeof(*state), GFP_KERNEL); if (!state) @@ -273,6 +275,12 @@ state->state = 0; memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); + ls = &state->lock_state; + init_MUTEX(&ls->ls_sema); + ls->ls_seqid = 0; /* arbitrary */ + ls->ls_id = -1; + memset(ls->ls_stateid.data, 0, sizeof(ls->ls_stateid.data)); + ls->ls_bitflag = 0; return state; } @@ -561,6 +569,146 @@ } /* +* Called with lsp->ls_sema held. +*/ +void +nfs4_increment_lock_seqid(u32 status, struct nfs4_lock_state *lsp) +{ + if (status == NFS_OK || seqid_mutating_err(status)) + lsp->ls_seqid++; +} + +/* + * Called in OP_LOCK + */ +struct nfs4_lock_state * +nfs4_get_lock_state(struct nfs4_state *sp, int *new_lock_owner) +{ + struct nfs4_lock_state *ls = &sp->lock_state; + + if (!test_and_set_bit(LK_STATE_IN_USE, &ls->ls_bitflag)) { + down(&ls->ls_sema); + ls->ls_id = nfs4_alloc_lockowner_id(sp->owner->so_client); + up(&ls->ls_sema); + *new_lock_owner = 1; + } else { + *new_lock_owner = 0; + } + return ls; +} +int +nfs4_test_lock_state(struct nfs4_state *sp) +{ +struct nfs4_lock_state *ls = &sp->lock_state; + + return(test_bit(LK_STATE_IN_USE, &ls->ls_bitflag)); +} + +/* +* Check to see if the request lock (type FL_UNLK) effects the fl lock. +* +* fl and request have the same posix owner +* +* return: +* 0 -> fl not effected by request +* 1 -> fl effected by request but not consumed +* 2 -> fl consumed by request +*/ + +int +nfs4_check_unlock(struct file_lock *fl, struct file_lock *request) +{ + int ret = 0; + + if ((fl->fl_end < request->fl_start) || (fl->fl_start > request->fl_end)) + /* request FL_UNLCK will not change this lock*/ + goto out; + ret = 1; + if (fl->fl_start < request->fl_start) + /* some of fl will remain */ + goto out; + if (fl->fl_start >= request->fl_start){ + if (fl->fl_end > request->fl_end) + /* some of fl will remain */ + goto out; + if (fl->fl_end <= request->fl_end) { + /* all of fl is consumed by request */ + ret = 2; + goto out; + } + } +out: + return ret; +} + +/* + * Called with sp->so_sema held + * + * nfs4_state pointer in file_lock->fl_u.nfs_fl.lock_state can only be + * set for on inode (one inode per nfs_state) + * + * nfs4_lock_state: one per nfs4_state. + * + * to decide to 'reap' (re-initialize) lock state: + * 1) search i_flock for file_locks with fl_u.nfs_fl.lock_state = to sp. + * 2) determine if unlock will consume found lock. + * if so, reap + * + * else, don't reap. + * + */ +void +nfs4_put_lock_state(struct inode *inode, struct file_lock *request, struct nfs4_state *sp) +{ + struct nfs4_lock_state *ls = &sp->lock_state; + struct file_lock *fl; + + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_POSIX)) + continue; + if (fl->fl_u.nfs_fl.lock_state == ls) + { + if (nfs4_check_unlock(fl,request) != 2) { + /* found lock not consumed, don't reap */ + return; + } + } + } + + /* either no locks found, or found lock totally consumed */ + ls->ls_seqid = 0; /* arbitrary */ + memset(ls->ls_stateid.data, 0, sizeof(ls->ls_stateid.data)); + clear_bit(LK_STATE_IN_USE, &ls->ls_bitflag); + ls->ls_id = -1; + return; +} + +/* +* Does unlock request effect any lock +* +* return +* +* 1 -> this unlock will change a file_lock in inode->i_flock +* 0 -> this unlock will NOT change any file_lock in inode->i_flock +*/ +int +nfs4_test_unlock(struct inode *inode, struct file_lock *request, struct nfs4_state *sp) +{ + struct nfs4_lock_state *ls = &sp->lock_state; + struct file_lock *fl; + + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_POSIX)) + continue; + if (fl->fl_u.nfs_fl.lock_state == ls) { + if(nfs4_check_unlock(fl,request)) + return 1; + } + } + return 0; +} + +/* * Local variables: * c-basic-offset: 8 * End: diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/fs/nfs/nfs4xdr.c linux-2.6.0-24-lock/fs/nfs/nfs4xdr.c --- linux-2.6.0-23-atomic_open2/fs/nfs/nfs4xdr.c 2003-11-11 18:25:03.000000000 -0500 +++ linux-2.6.0-24-lock/fs/nfs/nfs4xdr.c 2003-11-11 19:18:01.000000000 -0500 @@ -66,6 +66,10 @@ #define NFS4_MAXTAGLEN 0 #endif +/* lock,open owner id: + * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) + */ +#define owner_id_maxsz 1 + 1 #define compound_encode_hdr_maxsz 3 + (NFS4_MAXTAGLEN >> 2) #define compound_decode_hdr_maxsz 2 + (NFS4_MAXTAGLEN >> 2) #define op_encode_hdr_maxsz 1 @@ -214,6 +218,36 @@ decode_setclientid_confirm_maxsz + \ decode_putrootfh_maxsz + \ decode_fsinfo_maxsz +#define NFS4_enc_lock_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 1 + 2 + 2 + \ + 1 + 4 + 1 + 2 + \ + owner_id_maxsz +#define NFS4_dec_lock_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz + \ + op_decode_hdr_maxsz + \ + 2 + 2 + 1 + 2 + \ + owner_id_maxsz +#define NFS4_enc_lockt_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 2 + 2 + 2 + \ + owner_id_maxsz +#define NFS4_dec_lockt_sz NFS4_dec_lock_sz +#define NFS4_enc_locku_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 1 + 4 + 2 + 2 +#define NFS4_dec_locku_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz + \ + op_decode_hdr_maxsz + 4 + static struct { @@ -590,6 +624,80 @@ return 0; } +/* + * opcode,type,reclaim,offset,length,new_lock_owner = 32 + * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 + */ +static int +encode_lock(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_lock_opargs *opargs = arg->u.lock; + + RESERVE_SPACE(32); + WRITE32(OP_LOCK); + WRITE32(arg->type); + WRITE32(opargs->reclaim); + WRITE64(arg->offset); + WRITE64(arg->length); + WRITE32(opargs->new_lock_owner); + if (opargs->new_lock_owner){ + struct nfs_open_to_lock *ol = opargs->u.open_lock; + + RESERVE_SPACE(40); + WRITE32(ol->open_seqid); + WRITEMEM(&ol->open_stateid, sizeof(ol->open_stateid)); + WRITE32(ol->lock_seqid); + WRITE64(ol->lock_owner.clientid); + WRITE32(4); + WRITE32(ol->lock_owner.id); + } + else { + struct nfs_exist_lock *el = opargs->u.exist_lock; + + RESERVE_SPACE(20); + WRITEMEM(&el->stateid, sizeof(el->stateid)); + WRITE32(el->seqid); + } + + return 0; +} + +static int +encode_lockt(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_lowner *opargs = arg->u.lockt; + + RESERVE_SPACE(40); + WRITE32(OP_LOCKT); + WRITE32(arg->type); + WRITE64(arg->offset); + WRITE64(arg->length); + WRITE64(opargs->clientid); + WRITE32(4); + WRITE32(opargs->id); + + return 0; +} + +static int +encode_locku(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_locku_opargs *opargs = arg->u.locku; + + RESERVE_SPACE(44); + WRITE32(OP_LOCKU); + WRITE32(arg->type); + WRITE32(opargs->seqid); + WRITEMEM(&opargs->stateid, sizeof(opargs->stateid)); + WRITE64(arg->offset); + WRITE64(arg->length); + + return 0; +} + static int encode_lookup(struct xdr_stream *xdr, struct nfs4_lookup *lookup) { @@ -1131,6 +1239,71 @@ return status; } +/* + * Encode a LOCK request + */ +static int +nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_lock(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKT request + */ +static int +nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_lockt(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKU request + */ +static int +nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_locku(&xdr, args); +out: + return status; +} /* * Encode a READ request @@ -1406,6 +1579,31 @@ return 0; } +/* + * We create the owner, so we know a proper owner.id length is 4. + */ +static int +decode_lock_denied (struct xdr_stream *xdr, struct nfs_lock_denied *denied) +{ + uint32_t *p; + u32 namelen; + int status = 0; + + READ_BUF(36); + READ64(denied->offset); + READ64(denied->length); + READ32(denied->type); + READ64(denied->owner.clientid); + READ32(namelen); + if (namelen == 4) + READ32(denied->owner.id); + else + goto xdr_error; + + DECODE_TAIL; +} + + static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) { @@ -1955,6 +2153,43 @@ return status; return decode_change_info(xdr, link->ln_cinfo); } +static int +decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + uint32_t *p; + + res->status = decode_op_hdr(xdr, OP_LOCK); + if (res->status == 0) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(&res->u.stateid, sizeof(res->u.stateid)); + return 0; + } else if (res->status == NFS4ERR_DENIED) { + return decode_lock_denied(xdr, &res->u.denied); + } else + return res->status; +} + +static int +decode_lockt(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + res->status = decode_op_hdr(xdr, OP_LOCKT); + if (res->status == NFS4ERR_DENIED) + return decode_lock_denied(xdr, &res->u.denied); + return res->status; +} + +static int +decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + uint32_t *p; + + res->status = decode_op_hdr(xdr, OP_LOCKU); + if (res->status == 0) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(&res->u.stateid, sizeof(res->u.stateid)); + } + return res->status; +} static int decode_lookup(struct xdr_stream *xdr) @@ -2257,7 +2492,7 @@ READ_BUF(8 + sizeof(clp->cl_confirm.data)); READ64(clp->cl_clientid); COPYMEM(clp->cl_confirm.data, sizeof(clp->cl_confirm.data)); - } else if (nfserr == NFSERR_CLID_INUSE) { + } else if (nfserr == NFS4ERR_CLID_INUSE) { uint32_t len; /* skip netid string */ @@ -2516,6 +2751,71 @@ out: return status; } +/* + * Decode LOCK response + */ +static int +nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_lock(&xdr, res); +out: + return status; +} + +/* + * Decode LOCKT response + */ +static int +nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_lockt(&xdr, res); +out: + return status; +} + +/* + * Decode LOCKU response + */ +static int +nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_locku(&xdr, res); +out: + return status; +} /* * Decode SETATTR response @@ -2837,6 +3137,9 @@ PROC(RENEW, enc_renew, dec_renew), PROC(SETCLIENTID, enc_setclientid, dec_setclientid), PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), + PROC(LOCK, enc_lock, dec_lock), + PROC(LOCKT, enc_lockt, dec_lockt), + PROC(LOCKU, enc_locku, dec_locku), }; struct rpc_version nfs_version4 = { diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/fs/nfs/proc.c linux-2.6.0-24-lock/fs/nfs/proc.c --- linux-2.6.0-23-atomic_open2/fs/nfs/proc.c 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-24-lock/fs/nfs/proc.c 2003-11-12 00:24:44.000000000 -0500 @@ -42,6 +42,7 @@ #include #include #include +#include #include #define NFSDBG_FACILITY NFSDBG_PROC @@ -653,6 +654,12 @@ return 1; } +static int +nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) +{ + return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); +} + struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ @@ -689,4 +696,5 @@ .file_release = nfs_release, .request_init = nfs_request_init, .request_compatible = nfs_request_compatible, + .lock = nfs_proc_lock, }; diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/include/linux/nfs4.h linux-2.6.0-24-lock/include/linux/nfs4.h --- linux-2.6.0-23-atomic_open2/include/linux/nfs4.h 2003-11-11 18:25:03.000000000 -0500 +++ linux-2.6.0-24-lock/include/linux/nfs4.h 2003-11-11 19:18:01.000000000 -0500 @@ -296,6 +296,9 @@ NFSPROC4_CLNT_RENEW, NFSPROC4_CLNT_SETCLIENTID, NFSPROC4_CLNT_SETCLIENTID_CONFIRM, + NFSPROC4_CLNT_LOCK, + NFSPROC4_CLNT_LOCKT, + NFSPROC4_CLNT_LOCKU, }; #endif diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/include/linux/nfs_fs.h linux-2.6.0-24-lock/include/linux/nfs_fs.h --- linux-2.6.0-23-atomic_open2/include/linux/nfs_fs.h 2003-11-11 18:27:37.000000000 -0500 +++ linux-2.6.0-24-lock/include/linux/nfs_fs.h 2003-11-12 00:21:44.000000000 -0500 @@ -538,19 +538,37 @@ /* * struct nfs4_state maintains the client-side state for a given - * (state_owner,inode) tuple. + * (state_owner,inode) tuple (OPEN) or state_owner (LOCK). * + * OPEN: * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server, * we need to know how many files are open for reading or writing on a * given inode. This information too is stored here. + * + * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN) */ + +struct nfs4_lock_state { + struct semaphore ls_sema; /* for ls_seqid */ + u32 ls_seqid; + u32 ls_id; + nfs4_stateid ls_stateid; + unsigned long ls_bitflag; +}; + +/* bits for ls_bitflag */ +enum { + LK_STATE_IN_USE +}; + struct nfs4_state { struct list_head open_states; /* List of states for the same state_owner */ struct list_head inode_states; /* List of states for the same inode */ struct nfs4_state_owner *owner; /* Pointer to the open owner */ - struct inode *inode; /* Pointer to the inode */ - pid_t pid; /* Thread that called OPEN */ + struct nfs4_lock_state lock_state; /* One lock_state */ + struct inode *inode; /* Pointer to the inode */ + pid_t pid; /* Thread that called OPEN */ nfs4_stateid stateid; @@ -590,6 +608,13 @@ extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); +extern struct nfs4_state_owner * nfs4_get_lock_owner(struct nfs4_client *clp); +extern void nfs4_increment_lock_seqid(u32 status, struct nfs4_lock_state *ls); +extern struct nfs4_lock_state * nfs4_get_lock_state(struct nfs4_state *sp, int *new_lock_state); +extern void nfs4_put_lock_state(struct inode *inode, struct file_lock *request, struct nfs4_state *sp); +extern int nfs4_test_lock_state(struct nfs4_state *sp); +extern int nfs4_test_unlock(struct inode *inode, struct file_lock *request, struct nfs4_state *sp); + struct nfs4_mount_data; #else diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/include/linux/nfs_fs_i.h linux-2.6.0-24-lock/include/linux/nfs_fs_i.h --- linux-2.6.0-23-atomic_open2/include/linux/nfs_fs_i.h 2003-11-11 18:09:14.000000000 -0500 +++ linux-2.6.0-24-lock/include/linux/nfs_fs_i.h 2003-11-11 19:18:01.000000000 -0500 @@ -12,6 +12,9 @@ u32 state; u32 flags; struct nlm_host *host; +#ifdef CONFIG_NFS_V4 + struct nfs4_lock_state *lock_state; +#endif /*CONFIG_NFS_V4 */ }; /* diff -u --recursive --new-file linux-2.6.0-23-atomic_open2/include/linux/nfs_xdr.h linux-2.6.0-24-lock/include/linux/nfs_xdr.h --- linux-2.6.0-23-atomic_open2/include/linux/nfs_xdr.h 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-24-lock/include/linux/nfs_xdr.h 2003-11-12 00:19:32.000000000 -0500 @@ -159,7 +159,67 @@ __u32 status; nfs4_stateid stateid; }; +/* + * * Arguments to the lock,lockt, and locku call. + * */ +struct nfs_lowner { + __u64 clientid; + u32 id; +}; + +struct nfs_open_to_lock { + __u32 open_seqid; + nfs4_stateid open_stateid; + __u32 lock_seqid; + struct nfs_lowner lock_owner; +}; + +struct nfs_exist_lock { + nfs4_stateid stateid; + __u32 seqid; +}; + +struct nfs_lock_opargs { + __u32 reclaim; + __u32 new_lock_owner; + union { + struct nfs_open_to_lock *open_lock; + struct nfs_exist_lock *exist_lock; + } u; +}; + +struct nfs_locku_opargs { + __u32 seqid; + nfs4_stateid stateid; +}; + +struct nfs_lockargs { + struct nfs_fh * fh; + __u32 type; + __u64 offset; + __u64 length; + union { + struct nfs_lock_opargs *lock; /* LOCK */ + struct nfs_lowner *lockt; /* LOCKT */ + struct nfs_locku_opargs *locku; /* LOCKU */ + } u; +}; +struct nfs_lock_denied { + __u64 offset; + __u64 length; + __u32 type; + struct nfs_lowner owner; +}; + +struct nfs_lockres { + __u32 status; + union { + nfs4_stateid stateid;/* LOCK success, LOCKU */ + struct nfs_lock_denied denied; /* LOCK failed, LOCKT success */ + } u; + struct nfs_server * server; +}; /* * Arguments to the read call. @@ -685,6 +745,7 @@ int (*file_release) (struct inode *, struct file *); void (*request_init)(struct nfs_page *, struct file *); int (*request_compatible)(struct nfs_page *, struct file *, struct page *); + int (*lock)(struct file *, int, struct file_lock *); }; /*