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) ...@@ -65,11 +65,32 @@ fh_dup2(struct svc_fh *dst, struct svc_fh *src)
*dst = *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 static int
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{ {
struct svc_fh resfh; struct svc_fh resfh;
int accmode, status; int status;
fh_init(&resfh, NFS4_FHSIZE); fh_init(&resfh, NFS4_FHSIZE);
open->op_truncate = 0; open->op_truncate = 0;
...@@ -92,6 +113,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o ...@@ -92,6 +113,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
if (!status) { if (!status) {
set_change_info(&open->op_cinfo, current_fh); set_change_info(&open->op_cinfo, current_fh);
/* set reply cache */
fh_dup2(current_fh, &resfh); fh_dup2(current_fh, &resfh);
/* XXXJBF: keep a saved svc_fh struct instead?? */ /* XXXJBF: keep a saved svc_fh struct instead?? */
open->op_stateowner->so_replay.rp_openfh_len = 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 ...@@ -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_base,
resfh.fh_handle.fh_size); resfh.fh_handle.fh_size);
accmode = MAY_NOP; status = do_open_permission(rqstp, current_fh, open);
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);
} }
fh_put(&resfh); fh_put(&resfh);
return status; 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 * nfs4_unlock_state() called in encode
*/ */
...@@ -124,6 +169,13 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open ...@@ -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, (int)open->op_fname.len, open->op_fname.data,
open->op_stateowner); 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. */ /* This check required by spec. */
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
return nfserr_inval; return nfserr_inval;
...@@ -148,16 +200,30 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open ...@@ -148,16 +200,30 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
} }
if (status) if (status)
return 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, * This block of code will (1) set CURRENT_FH to the file being opened,
* creating it if necessary, (2) set open->op_cinfo, * creating it if necessary, (2) set open->op_cinfo,
* (3) set open->op_truncate if the file is to be truncated * (3) set open->op_truncate if the file is to be truncated
* after opening, (4) do permission checking. * after opening, (4) do permission checking.
*/ */
status = do_open_lookup(rqstp, current_fh, open); status = do_open_lookup(rqstp, current_fh, open);
if (status) if (status)
return 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 * nfsd4_process_open2() does the actual opening of the file. If
* successful, it (1) truncates the file if open->op_truncate was * 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 ...@@ -414,6 +480,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read
int status; int status;
/* no need to check permission - this will be done in nfsd_read() */ /* 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) if (read->rd_offset >= OFFSET_MAX)
return nfserr_inval; return nfserr_inval;
...@@ -537,10 +605,13 @@ static inline int ...@@ -537,10 +605,13 @@ static inline int
nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr) nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr)
{ {
struct nfs4_stateid *stp; struct nfs4_stateid *stp;
int status = nfserr_nofilehandle; int status = nfs_ok;
if (nfs4_in_grace())
return nfserr_grace;
if (!current_fh->fh_dentry) if (!current_fh->fh_dentry)
goto out; return nfserr_nofilehandle;
status = nfs_ok; status = nfs_ok;
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { 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 ...@@ -579,6 +650,9 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
u32 *p; u32 *p;
int status = nfs_ok; int status = nfs_ok;
if (nfs4_in_grace())
return nfserr_grace;
/* no need to check permission - this will be done in nfsd_write() */ /* no need to check permission - this will be done in nfsd_write() */
if (write->wr_offset >= OFFSET_MAX) if (write->wr_offset >= OFFSET_MAX)
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
/* Globals */ /* Globals */
time_t boot_time; time_t boot_time;
static time_t grace_end = 0;
static u32 current_clientid = 1; static u32 current_clientid = 1;
static u32 current_ownerid; static u32 current_ownerid;
static u32 current_fileid; static u32 current_fileid;
...@@ -1090,7 +1091,7 @@ nfsd4_process_open1(struct nfsd4_open *open) ...@@ -1090,7 +1091,7 @@ nfsd4_process_open1(struct nfsd4_open *open)
status = nfserr_stale_clientid; status = nfserr_stale_clientid;
if (STALE_CLIENTID(&open->op_clientid)) if (STALE_CLIENTID(&open->op_clientid))
goto out; return status;
strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner); strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner);
if (find_openstateowner_str(strhashval, open, &sop)) { if (find_openstateowner_str(strhashval, open, &sop)) {
...@@ -1111,7 +1112,7 @@ nfsd4_process_open1(struct nfsd4_open *open) ...@@ -1111,7 +1112,7 @@ nfsd4_process_open1(struct nfsd4_open *open)
} }
/* replay: indicate to calling function */ /* replay: indicate to calling function */
status = NFSERR_REPLAY_ME; status = NFSERR_REPLAY_ME;
goto out; return status;
} }
if (sop->so_confirmed) { if (sop->so_confirmed) {
if (open->op_seqid == sop->so_seqid + 1) { if (open->op_seqid == sop->so_seqid + 1) {
...@@ -1149,6 +1150,8 @@ nfsd4_process_open1(struct nfsd4_open *open) ...@@ -1149,6 +1150,8 @@ nfsd4_process_open1(struct nfsd4_open *open)
renew: renew:
renew_client(sop->so_client); renew_client(sop->so_client);
out: out:
if (status && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
status = nfserr_reclaim_bad;
return status; return status;
} }
/* /*
...@@ -1159,7 +1162,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1159,7 +1162,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
{ {
struct iattr iattr; struct iattr iattr;
struct nfs4_stateowner *sop = open->op_stateowner; struct nfs4_stateowner *sop = open->op_stateowner;
struct nfs4_file *fp; struct nfs4_file *fp = NULL;
struct inode *ino; struct inode *ino;
unsigned int fi_hashval; unsigned int fi_hashval;
struct nfs4_stateid *stq, *stp = NULL; struct nfs4_stateid *stq, *stp = NULL;
...@@ -1167,7 +1170,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1167,7 +1170,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
status = nfserr_resource; status = nfserr_resource;
if (!sop) if (!sop)
goto out; return status;
ino = current_fh->fh_dentry->d_inode; 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 ...@@ -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)) if (fp && list_empty(&fp->fi_perfile))
release_file(fp); 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. * 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 ...@@ -1270,6 +1284,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
kfree(stp); kfree(stp);
goto out; goto out;
} }
static struct work_struct laundromat_work; static struct work_struct laundromat_work;
static void laundromat_main(void *); static void laundromat_main(void *);
static DECLARE_WORK(laundromat_work, laundromat_main, NULL); 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 ...@@ -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_offset,
(long long) lock->lk_length); (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)) if (check_lock_length(lock->lk_offset, lock->lk_length))
return nfserr_inval; return nfserr_inval;
...@@ -1983,8 +2003,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock ...@@ -1983,8 +2003,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
CHECK_FH | OPEN_STATE, CHECK_FH | OPEN_STATE,
&open_sop, &open_stp, &open_sop, &open_stp,
&lock->v.new.clientid); &lock->v.new.clientid);
if (status) if (status) {
if (lock->lk_reclaim)
status = nfserr_reclaim_bad;
goto out; goto out;
}
/* create lockowner and lock stateid */ /* create lockowner and lock stateid */
fp = open_stp->st_file; fp = open_stp->st_file;
strhashval = lock_ownerstr_hashval(fp->fi_inode, 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 ...@@ -2119,6 +2142,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
unsigned int strhashval; unsigned int strhashval;
int status; int status;
if (nfs4_in_grace())
return nfserr_grace;
if (check_lock_length(lockt->lt_offset, lockt->lt_length)) if (check_lock_length(lockt->lt_offset, lockt->lt_length))
return nfserr_inval; return nfserr_inval;
...@@ -2343,6 +2369,7 @@ void ...@@ -2343,6 +2369,7 @@ void
nfs4_state_init(void) nfs4_state_init(void)
{ {
int i; int i;
time_t start = get_seconds();
if (nfs4_init) if (nfs4_init)
return; return;
...@@ -2373,13 +2400,27 @@ nfs4_state_init(void) ...@@ -2373,13 +2400,27 @@ nfs4_state_init(void)
INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&close_lru);
INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&client_lru);
init_MUTEX(&client_sema); init_MUTEX(&client_sema);
boot_time = get_seconds(); boot_time = start;
grace_end = start + NFSD_LEASE_TIME;
INIT_WORK(&laundromat_work,laundromat_main, NULL); INIT_WORK(&laundromat_work,laundromat_main, NULL);
schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ); schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
nfs4_init = 1; 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 static void
__nfs4_state_shutdown(void) __nfs4_state_shutdown(void)
{ {
......
...@@ -196,6 +196,9 @@ void nfsd_lockd_shutdown(void); ...@@ -196,6 +196,9 @@ void nfsd_lockd_shutdown(void);
#define nfserr_openmode __constant_htonl(NFSERR_OPENMODE) #define nfserr_openmode __constant_htonl(NFSERR_OPENMODE)
#define nfserr_locks_held __constant_htonl(NFSERR_LOCKS_HELD) #define nfserr_locks_held __constant_htonl(NFSERR_LOCKS_HELD)
#define nfserr_op_illegal __constant_htonl(NFSERR_OP_ILLEGAL) #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 */ /* error codes for internal use */
/* if a request fails due to kmalloc failure, it gets dropped. /* 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, ...@@ -215,4 +215,6 @@ extern int nfs4_share_conflict(struct svc_fh *current_fh,
unsigned int deny_type); unsigned int deny_type);
extern void nfs4_lock_state(void); extern void nfs4_lock_state(void);
extern void nfs4_unlock_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 */ #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