Commit 3d694271 authored by Dai Ngo's avatar Dai Ngo Committed by Chuck Lever

NFSD: add support for share reservation conflict to courteous server

This patch allows expired client with open state to be in COURTESY
state. Share/access conflict with COURTESY client is resolved by
setting COURTESY client to EXPIRABLE state, schedule laundromat
to run and returning nfserr_jukebox to the request client.
Reviewed-by: default avatarJ. Bruce Fields <bfields@fieldses.org>
Signed-off-by: default avatarDai Ngo <dai.ngo@oracle.com>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 66af2579
...@@ -694,6 +694,57 @@ static unsigned int file_hashval(struct svc_fh *fh) ...@@ -694,6 +694,57 @@ static unsigned int file_hashval(struct svc_fh *fh)
static struct hlist_head file_hashtbl[FILE_HASH_SIZE]; static struct hlist_head file_hashtbl[FILE_HASH_SIZE];
/*
* Check if courtesy clients have conflicting access and resolve it if possible
*
* access: is op_share_access if share_access is true.
* Check if access mode, op_share_access, would conflict with
* the current deny mode of the file 'fp'.
* access: is op_share_deny if share_access is false.
* Check if the deny mode, op_share_deny, would conflict with
* current access of the file 'fp'.
* stp: skip checking this entry.
* new_stp: normal open, not open upgrade.
*
* Function returns:
* false - access/deny mode conflict with normal client.
* true - no conflict or conflict with courtesy client(s) is resolved.
*/
static bool
nfs4_resolve_deny_conflicts_locked(struct nfs4_file *fp, bool new_stp,
struct nfs4_ol_stateid *stp, u32 access, bool share_access)
{
struct nfs4_ol_stateid *st;
bool resolvable = true;
unsigned char bmap;
struct nfsd_net *nn;
struct nfs4_client *clp;
lockdep_assert_held(&fp->fi_lock);
list_for_each_entry(st, &fp->fi_stateids, st_perfile) {
/* ignore lock stateid */
if (st->st_openstp)
continue;
if (st == stp && new_stp)
continue;
/* check file access against deny mode or vice versa */
bmap = share_access ? st->st_deny_bmap : st->st_access_bmap;
if (!(access & bmap_to_share_mode(bmap)))
continue;
clp = st->st_stid.sc_client;
if (try_to_expire_client(clp))
continue;
resolvable = false;
break;
}
if (resolvable) {
clp = stp->st_stid.sc_client;
nn = net_generic(clp->net, nfsd_net_id);
mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
}
return resolvable;
}
static void static void
__nfs4_file_get_access(struct nfs4_file *fp, u32 access) __nfs4_file_get_access(struct nfs4_file *fp, u32 access)
{ {
...@@ -4968,7 +5019,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, ...@@ -4968,7 +5019,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,
static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp,
struct nfsd4_open *open) struct nfsd4_open *open, bool new_stp)
{ {
struct nfsd_file *nf = NULL; struct nfsd_file *nf = NULL;
__be32 status; __be32 status;
...@@ -4984,6 +5035,13 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, ...@@ -4984,6 +5035,13 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
*/ */
status = nfs4_file_check_deny(fp, open->op_share_deny); status = nfs4_file_check_deny(fp, open->op_share_deny);
if (status != nfs_ok) { if (status != nfs_ok) {
if (status != nfserr_share_denied) {
spin_unlock(&fp->fi_lock);
goto out;
}
if (nfs4_resolve_deny_conflicts_locked(fp, new_stp,
stp, open->op_share_deny, false))
status = nfserr_jukebox;
spin_unlock(&fp->fi_lock); spin_unlock(&fp->fi_lock);
goto out; goto out;
} }
...@@ -4991,6 +5049,13 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, ...@@ -4991,6 +5049,13 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
/* set access to the file */ /* set access to the file */
status = nfs4_file_get_access(fp, open->op_share_access); status = nfs4_file_get_access(fp, open->op_share_access);
if (status != nfs_ok) { if (status != nfs_ok) {
if (status != nfserr_share_denied) {
spin_unlock(&fp->fi_lock);
goto out;
}
if (nfs4_resolve_deny_conflicts_locked(fp, new_stp,
stp, open->op_share_access, true))
status = nfserr_jukebox;
spin_unlock(&fp->fi_lock); spin_unlock(&fp->fi_lock);
goto out; goto out;
} }
...@@ -5037,21 +5102,29 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, ...@@ -5037,21 +5102,29 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
} }
static __be32 static __be32
nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp,
struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp,
struct nfsd4_open *open)
{ {
__be32 status; __be32 status;
unsigned char old_deny_bmap = stp->st_deny_bmap; unsigned char old_deny_bmap = stp->st_deny_bmap;
if (!test_access(open->op_share_access, stp)) if (!test_access(open->op_share_access, stp))
return nfs4_get_vfs_file(rqstp, fp, cur_fh, stp, open); return nfs4_get_vfs_file(rqstp, fp, cur_fh, stp, open, false);
/* test and set deny mode */ /* test and set deny mode */
spin_lock(&fp->fi_lock); spin_lock(&fp->fi_lock);
status = nfs4_file_check_deny(fp, open->op_share_deny); status = nfs4_file_check_deny(fp, open->op_share_deny);
if (status == nfs_ok) { if (status == nfs_ok) {
set_deny(open->op_share_deny, stp); if (status != nfserr_share_denied) {
fp->fi_share_deny |= set_deny(open->op_share_deny, stp);
fp->fi_share_deny |=
(open->op_share_deny & NFS4_SHARE_DENY_BOTH); (open->op_share_deny & NFS4_SHARE_DENY_BOTH);
} else {
if (nfs4_resolve_deny_conflicts_locked(fp, false,
stp, open->op_share_deny, false))
status = nfserr_jukebox;
}
} }
spin_unlock(&fp->fi_lock); spin_unlock(&fp->fi_lock);
...@@ -5392,7 +5465,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -5392,7 +5465,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
goto out; goto out;
} }
} else { } else {
status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open); status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open, true);
if (status) { if (status) {
stp->st_stid.sc_type = NFS4_CLOSED_STID; stp->st_stid.sc_type = NFS4_CLOSED_STID;
release_open_stateid(stp); release_open_stateid(stp);
...@@ -5626,6 +5699,26 @@ static void nfsd4_ssc_expire_umount(struct nfsd_net *nn) ...@@ -5626,6 +5699,26 @@ static void nfsd4_ssc_expire_umount(struct nfsd_net *nn)
} }
#endif #endif
static bool
nfs4_has_any_locks(struct nfs4_client *clp)
{
int i;
struct nfs4_stateowner *so;
spin_lock(&clp->cl_lock);
for (i = 0; i < OWNER_HASH_SIZE; i++) {
list_for_each_entry(so, &clp->cl_ownerstr_hashtbl[i],
so_strhash) {
if (so->so_is_open_owner)
continue;
spin_unlock(&clp->cl_lock);
return true;
}
}
spin_unlock(&clp->cl_lock);
return false;
}
/* /*
* place holder for now, no check for lock blockers yet * place holder for now, no check for lock blockers yet
*/ */
...@@ -5633,8 +5726,8 @@ static bool ...@@ -5633,8 +5726,8 @@ static bool
nfs4_anylock_blockers(struct nfs4_client *clp) nfs4_anylock_blockers(struct nfs4_client *clp)
{ {
if (atomic_read(&clp->cl_delegs_in_recall) || if (atomic_read(&clp->cl_delegs_in_recall) ||
client_has_openowners(clp) || !list_empty(&clp->async_copies) ||
!list_empty(&clp->async_copies)) nfs4_has_any_locks(clp))
return true; return true;
return false; return false;
} }
......
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