}
}
+/*
+ * 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)
{
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);
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));
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
#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
}
+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)
{
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
return status;
}
+/*
+ * 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
*/
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),
NFSPROC4_CLNT_COMMIT,
NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM,
+ NFSPROC4_CLNT_OPEN_RECLAIM,
NFSPROC4_CLNT_CLOSE,
NFSPROC4_CLNT_SETATTR,
NFSPROC4_CLNT_FSINFO,
/* 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 *);
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
nfs4_stateid stateid;
};
+/*
+ * 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.
*/