From: Trond Myklebust NFSv4: Preparation for the server reboot recovery code. --- fs/nfs/nfs4proc.c | 55 +++++++++++++++++++++++++-- fs/nfs/nfs4state.c | 89 ++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 include/linux/nfs_fs.h | 2 include/linux/nfs_xdr.h | 13 ++++++ 6 files changed, 253 insertions(+), 4 deletions(-) diff -puN fs/nfs/nfs4proc.c~nfs-23-open_reclaim fs/nfs/nfs4proc.c --- 25/fs/nfs/nfs4proc.c~nfs-23-open_reclaim 2004-01-09 22:16:21.000000000 -0800 +++ 25-akpm/fs/nfs/nfs4proc.c 2004-01-09 22:16:21.000000000 -0800 @@ -458,6 +458,54 @@ process_cinfo(struct nfs4_change_info *i } } +/* + * OPEN_RECLAIM: + * reclaim state on the server after a reboot. + * Assumes caller is holding the sp->so_sem + */ +int +nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr = { + .valid = 0, + }; + struct nfs4_change_info d_cinfo; + struct nfs4_getattr f_getattr = { + .gt_bmval = nfs4_fattr_bitmap, + .gt_attrs = &fattr, + }; + + struct nfs_open_reclaimargs o_arg = { + .fh = NFS_FH(inode), + .seqid = sp->so_seqid, + .id = sp->so_id, + .share_access = state->state, + .clientid = server->nfs4_state->cl_clientid, + .claim = NFS4_OPEN_CLAIM_PREVIOUS, + .f_getattr = &f_getattr, + }; + struct nfs_openres o_res = { + .cinfo = &d_cinfo, + .f_getattr = &f_getattr, + .server = server, /* Grrr */ + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_RECLAIM], + .rpc_argp = &o_arg, + .rpc_resp = &o_res, + .rpc_cred = sp->so_cred, + }; + int status; + + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_seqid(status, sp); + /* Update the inode attributes */ + nfs_refresh_inode(inode, &fattr); + return status; +} + struct nfs4_state * nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { @@ -523,10 +571,9 @@ nfs4_do_open(struct inode *dir, struct q o_arg.id = sp->so_id; status = rpc_call_sync(server->client, &msg, 0); - if (status) { - goto out_up; - } nfs4_increment_seqid(status, sp); + if (status) + goto out_up; process_cinfo(&d_cinfo, &d_attr); nfs_refresh_inode(dir, &d_attr); @@ -555,9 +602,9 @@ nfs4_do_open(struct inode *dir, struct q memcpy(&oc_arg.stateid, &o_res.stateid, sizeof(oc_arg.stateid)); status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_seqid(status, sp); if (status) goto out_up; - nfs4_increment_seqid(status, sp); memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid)); } else memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); diff -puN fs/nfs/nfs4state.c~nfs-23-open_reclaim fs/nfs/nfs4state.c --- 25/fs/nfs/nfs4state.c~nfs-23-open_reclaim 2004-01-09 22:16:21.000000000 -0800 +++ 25-akpm/fs/nfs/nfs4state.c 2004-01-09 22:16:21.000000000 -0800 @@ -382,6 +382,95 @@ nfs4_increment_seqid(int status, struct sp->so_seqid++; } +static int reclaimer(void *); +struct reclaimer_args { + struct nfs4_client *clp; + struct completion complete; +}; + +/* + * State recovery routine + */ +void +nfs4_recover_state(struct nfs4_client *clp) +{ + struct reclaimer_args args = { + .clp = clp, + }; + init_completion(&args.complete); + + down_read(&clp->cl_sem); + if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + goto out_failed; + wait_for_completion(&args.complete); + return; +out_failed: + up_read(&clp->cl_sem); +} + +static void +nfs4_reclaim_open_state(struct nfs4_state_owner *sp) +{ + struct nfs4_state *state; + int status; + + list_for_each_entry(state, &sp->so_states, open_states) { + status = nfs4_open_reclaim(sp, state); + if (status) { + /* + * Open state on this file cannot be recovered + * All we can do is revert to using the zero stateid. + */ + memset(state->stateid.data, 0, + sizeof(state->stateid.data)); + /* Mark the file as being 'closed' */ + state->state = 0; + } + } +} + +static int +reclaimer(void *ptr) +{ + struct reclaimer_args *args = (struct reclaimer_args *)ptr; + struct nfs4_client *clp = args->clp; + struct nfs4_state_owner *sp; + int status; + + daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); + allow_signal(SIGKILL); + + complete(&args->complete); + + /* Are there any NFS mounts out there? */ + if (list_empty(&clp->cl_superblocks)) + goto out; + status = nfs4_proc_setclientid(clp, 0, 0); + if (status) + goto out_error; + status = nfs4_proc_setclientid_confirm(clp); + if (status) + goto out_error; + spin_lock(&clp->cl_lock); + list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + atomic_inc(&sp->so_count); + spin_unlock(&clp->cl_lock); + down(&sp->so_sema); + nfs4_reclaim_open_state(sp); + up(&sp->so_sema); + nfs4_put_state_owner(sp); + spin_lock(&clp->cl_lock); + } + spin_unlock(&clp->cl_lock); +out: + up_read(&clp->cl_sem); + return 0; +out_error: + printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n", + NIPQUAD(clp->cl_addr.s_addr)); + goto out; +} + /* * Local variables: * c-basic-offset: 8 diff -puN fs/nfs/nfs4xdr.c~nfs-23-open_reclaim fs/nfs/nfs4xdr.c --- 25/fs/nfs/nfs4xdr.c~nfs-23-open_reclaim 2004-01-09 22:16:21.000000000 -0800 +++ 25-akpm/fs/nfs/nfs4xdr.c 2004-01-09 22:16:21.000000000 -0800 @@ -166,6 +166,16 @@ static int nfs_stat_to_errno(int); #define NFS4_dec_open_confirm_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4 +#define NFS4_enc_open_reclaim_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + \ + 11 + \ + encode_getattr_maxsz +#define NFS4_dec_open_reclaim_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + 4 + 5 + 2 + 3 + \ + decode_getattr_maxsz #define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5 @@ -667,6 +677,41 @@ encode_open_confirm(struct xdr_stream *x static int +encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg) +{ + uint32_t *p; + + /* + * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4, + * owner 4, opentype 4, claim 4, delegation_type 4 = 44 + */ + RESERVE_SPACE(44); + WRITE32(OP_OPEN); + WRITE32(arg->seqid); + switch (arg->share_access) { + case FMODE_READ: + WRITE32(NFS4_SHARE_ACCESS_READ); + break; + case FMODE_WRITE: + WRITE32(NFS4_SHARE_ACCESS_WRITE); + break; + case FMODE_READ|FMODE_WRITE: + WRITE32(NFS4_SHARE_ACCESS_BOTH); + break; + default: + BUG(); + } + WRITE32(0); /* for linux, share_deny = 0 always */ + WRITE64(arg->clientid); + WRITE32(4); + WRITE32(arg->id); + WRITE32(NFS4_OPEN_NOCREATE); + WRITE32(NFS4_OPEN_CLAIM_PREVIOUS); + WRITE32(NFS4_OPEN_DELEGATE_NONE); + return 0; +} + +static int encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh) { int len = fh->size; @@ -1058,6 +1103,32 @@ out: return status; } +/* + * Encode an OPEN request + */ +static int +nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, + struct nfs_open_reclaimargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + 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_open_reclaim(&xdr, args); + if (status) + goto out; + status = encode_getattr(&xdr, args->f_getattr); +out: + return status; +} + /* * Encode a READ request @@ -2418,6 +2489,31 @@ out: } /* + * Decode OPEN_RECLAIM response + */ +static int +nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *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_open(&xdr, res); + if (status) + goto out; + status = decode_getattr(&xdr, res->f_getattr, res->server); +out: + return status; +} + +/* * Decode SETATTR response */ static int @@ -2730,6 +2826,7 @@ struct rpc_procinfo nfs4_procedures[] = PROC(COMMIT, enc_commit, dec_commit), PROC(OPEN, enc_open, dec_open), PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), + PROC(OPEN_RECLAIM, enc_open_reclaim, dec_open_reclaim), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), diff -puN include/linux/nfs4.h~nfs-23-open_reclaim include/linux/nfs4.h --- 25/include/linux/nfs4.h~nfs-23-open_reclaim 2004-01-09 22:16:21.000000000 -0800 +++ 25-akpm/include/linux/nfs4.h 2004-01-09 22:16:21.000000000 -0800 @@ -289,6 +289,7 @@ enum { NFSPROC4_CLNT_COMMIT, NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN_CONFIRM, + NFSPROC4_CLNT_OPEN_RECLAIM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, diff -puN include/linux/nfs_fs.h~nfs-23-open_reclaim include/linux/nfs_fs.h --- 25/include/linux/nfs_fs.h~nfs-23-open_reclaim 2004-01-09 22:16:21.000000000 -0800 +++ 25-akpm/include/linux/nfs_fs.h 2004-01-09 22:16:21.000000000 -0800 @@ -555,6 +555,7 @@ struct nfs4_state { /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); +extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); @@ -573,6 +574,7 @@ extern void nfs4_put_state_owner(struct extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); +extern void nfs4_recover_state(struct nfs4_client *); struct nfs4_mount_data; #else diff -puN include/linux/nfs_xdr.h~nfs-23-open_reclaim include/linux/nfs_xdr.h --- 25/include/linux/nfs_xdr.h~nfs-23-open_reclaim 2004-01-09 22:16:21.000000000 -0800 +++ 25-akpm/include/linux/nfs_xdr.h 2004-01-09 22:16:21.000000000 -0800 @@ -134,6 +134,19 @@ struct nfs_open_confirmres { }; /* + * Arguments to the open_reclaim call. + */ +struct nfs_open_reclaimargs { + struct nfs_fh * fh; + __u64 clientid; + __u32 seqid; + __u32 id; + __u32 share_access; + __u32 claim; + struct nfs4_getattr * f_getattr; +}; + +/* * Arguments to the close call. */ struct nfs_closeargs { _