Commit cba16655 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] kNFSdv4: Implement server-side reboot recovery (mostly)

From: NeilBrown <neilb@cse.unsw.edu.au>

From: "J. Bruce Fields" <bfields@fieldses.org>

From: Andros: Implement server-side reboot recovery (server now handles
open and lock reclaims).  Not completely to spec: we don't yet store the
state in stable storage that would be required to recover correctly in
certain situations.
parent b889edc7
......@@ -65,11 +65,32 @@ fh_dup2(struct svc_fh *dst, struct svc_fh *src)
*dst = *src;
}
static int
do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
int accmode, status;
if (open->op_truncate &&
!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
return nfserr_inval;
accmode = MAY_NOP;
if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
accmode = MAY_READ;
if (open->op_share_deny & NFS4_SHARE_ACCESS_WRITE)
accmode |= (MAY_WRITE | MAY_TRUNC);
accmode |= MAY_OWNER_OVERRIDE;
status = fh_verify(rqstp, current_fh, S_IFREG, accmode);
return status;
}
static int
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
struct svc_fh resfh;
int accmode, status;
int status;
fh_init(&resfh, NFS4_FHSIZE);
open->op_truncate = 0;
......@@ -92,6 +113,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
if (!status) {
set_change_info(&open->op_cinfo, current_fh);
/* set reply cache */
fh_dup2(current_fh, &resfh);
/* XXXJBF: keep a saved svc_fh struct instead?? */
open->op_stateowner->so_replay.rp_openfh_len =
......@@ -100,19 +123,41 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
&resfh.fh_handle.fh_base,
resfh.fh_handle.fh_size);
accmode = MAY_NOP;
if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
accmode = MAY_READ;
if (open->op_share_deny & NFS4_SHARE_ACCESS_WRITE)
accmode |= (MAY_WRITE | MAY_TRUNC);
accmode |= MAY_OWNER_OVERRIDE;
status = fh_verify(rqstp, current_fh, S_IFREG, accmode);
status = do_open_permission(rqstp, current_fh, open);
}
fh_put(&resfh);
return status;
}
static int
do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
int status;
dprintk("NFSD: do_open_fhandle\n");
/* we don't know the target directory, and therefore can not
* set the change info
*/
memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info));
/* set replay cache */
open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size;
memcpy(open->op_stateowner->so_replay.rp_openfh,
&current_fh->fh_handle.fh_base,
current_fh->fh_handle.fh_size);
open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
!open->op_iattr.ia_size;
status = do_open_permission(rqstp, current_fh, open);
return status;
}
/*
* nfs4_unlock_state() called in encode
*/
......@@ -124,6 +169,13 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
(int)open->op_fname.len, open->op_fname.data,
open->op_stateowner);
if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
return nfserr_grace;
if (nfs4_in_no_grace() &&
open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
return nfserr_no_grace;
/* This check required by spec. */
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
return nfserr_inval;
......@@ -148,16 +200,30 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
}
if (status)
return status;
if (open->op_claim_type == NFS4_OPEN_CLAIM_NULL) {
/*
* This block of code will (1) set CURRENT_FH to the file being opened,
* creating it if necessary, (2) set open->op_cinfo,
* (3) set open->op_truncate if the file is to be truncated
* after opening, (4) do permission checking.
*/
status = do_open_lookup(rqstp, current_fh, open);
if (status)
return status;
status = do_open_lookup(rqstp, current_fh, open);
if (status)
return status;
} else if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
/*
* The CURRENT_FH is already set to the file being opened. This
* block of code will (1) set open->op_cinfo, (2) set
* open->op_truncate if the file is to be truncated after opening,
* (3) do permission checking.
*/
status = do_open_fhandle(rqstp, current_fh, open);
if (status)
return status;
} else {
printk("NFSD: unsupported OPEN claim type\n");
return nfserr_inval;
}
/*
* nfsd4_process_open2() does the actual opening of the file. If
* successful, it (1) truncates the file if open->op_truncate was
......@@ -414,6 +480,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read
int status;
/* no need to check permission - this will be done in nfsd_read() */
if (nfs4_in_grace())
return nfserr_grace;
if (read->rd_offset >= OFFSET_MAX)
return nfserr_inval;
......@@ -537,10 +605,13 @@ static inline int
nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr)
{
struct nfs4_stateid *stp;
int status = nfserr_nofilehandle;
int status = nfs_ok;
if (nfs4_in_grace())
return nfserr_grace;
if (!current_fh->fh_dentry)
goto out;
return nfserr_nofilehandle;
status = nfs_ok;
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
......@@ -579,6 +650,9 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
u32 *p;
int status = nfs_ok;
if (nfs4_in_grace())
return nfserr_grace;
/* no need to check permission - this will be done in nfsd_write() */
if (write->wr_offset >= OFFSET_MAX)
......
......@@ -52,6 +52,7 @@
/* Globals */
time_t boot_time;
static time_t grace_end = 0;
static u32 current_clientid = 1;
static u32 current_ownerid;
static u32 current_fileid;
......@@ -1090,7 +1091,7 @@ nfsd4_process_open1(struct nfsd4_open *open)
status = nfserr_stale_clientid;
if (STALE_CLIENTID(&open->op_clientid))
goto out;
return status;
strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner);
if (find_openstateowner_str(strhashval, open, &sop)) {
......@@ -1111,7 +1112,7 @@ nfsd4_process_open1(struct nfsd4_open *open)
}
/* replay: indicate to calling function */
status = NFSERR_REPLAY_ME;
goto out;
return status;
}
if (sop->so_confirmed) {
if (open->op_seqid == sop->so_seqid + 1) {
......@@ -1149,6 +1150,8 @@ nfsd4_process_open1(struct nfsd4_open *open)
renew:
renew_client(sop->so_client);
out:
if (status && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
status = nfserr_reclaim_bad;
return status;
}
/*
......@@ -1159,7 +1162,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
{
struct iattr iattr;
struct nfs4_stateowner *sop = open->op_stateowner;
struct nfs4_file *fp;
struct nfs4_file *fp = NULL;
struct inode *ino;
unsigned int fi_hashval;
struct nfs4_stateid *stq, *stp = NULL;
......@@ -1167,7 +1170,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
status = nfserr_resource;
if (!sop)
goto out;
return status;
ino = current_fh->fh_dentry->d_inode;
......@@ -1258,6 +1261,17 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
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.
*/
......@@ -1270,6 +1284,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
kfree(stp);
goto out;
}
static struct work_struct laundromat_work;
static void laundromat_main(void *);
static DECLARE_WORK(laundromat_work, laundromat_main, NULL);
......@@ -1954,6 +1969,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
(long long) lock->lk_offset,
(long long) lock->lk_length);
if (nfs4_in_grace() && !lock->lk_reclaim)
return nfserr_grace;
if (nfs4_in_no_grace() && lock->lk_reclaim)
return nfserr_no_grace;
if (check_lock_length(lock->lk_offset, lock->lk_length))
return nfserr_inval;
......@@ -1983,8 +2003,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
CHECK_FH | OPEN_STATE,
&open_sop, &open_stp,
&lock->v.new.clientid);
if (status)
if (status) {
if (lock->lk_reclaim)
status = nfserr_reclaim_bad;
goto out;
}
/* create lockowner and lock stateid */
fp = open_stp->st_file;
strhashval = lock_ownerstr_hashval(fp->fi_inode,
......@@ -2119,6 +2142,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
unsigned int strhashval;
int status;
if (nfs4_in_grace())
return nfserr_grace;
if (check_lock_length(lockt->lt_offset, lockt->lt_length))
return nfserr_inval;
......@@ -2343,6 +2369,7 @@ void
nfs4_state_init(void)
{
int i;
time_t start = get_seconds();
if (nfs4_init)
return;
......@@ -2373,13 +2400,27 @@ nfs4_state_init(void)
INIT_LIST_HEAD(&close_lru);
INIT_LIST_HEAD(&client_lru);
init_MUTEX(&client_sema);
boot_time = get_seconds();
boot_time = start;
grace_end = start + NFSD_LEASE_TIME;
INIT_WORK(&laundromat_work,laundromat_main, NULL);
schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
nfs4_init = 1;
}
int
nfs4_in_grace(void)
{
return time_before(get_seconds(), (unsigned long)grace_end);
}
int
nfs4_in_no_grace(void)
{
return (grace_end < get_seconds());
}
static void
__nfs4_state_shutdown(void)
{
......
......@@ -196,6 +196,9 @@ void nfsd_lockd_shutdown(void);
#define nfserr_openmode __constant_htonl(NFSERR_OPENMODE)
#define nfserr_locks_held __constant_htonl(NFSERR_LOCKS_HELD)
#define nfserr_op_illegal __constant_htonl(NFSERR_OP_ILLEGAL)
#define nfserr_grace __constant_htonl(NFSERR_GRACE)
#define nfserr_no_grace __constant_htonl(NFSERR_NO_GRACE)
#define nfserr_reclaim_bad __constant_htonl(NFSERR_RECLAIM_BAD)
/* error codes for internal use */
/* if a request fails due to kmalloc failure, it gets dropped.
......
......@@ -215,4 +215,6 @@ extern int nfs4_share_conflict(struct svc_fh *current_fh,
unsigned int deny_type);
extern void nfs4_lock_state(void);
extern void nfs4_unlock_state(void);
extern int nfs4_in_grace(void);
extern int nfs4_in_no_grace(void);
#endif /* NFSD4_STATE_H */
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