NFSD: add support for share reservation conflict to courteous server
[ Upstream commit 3d69427151806656abf129342028f3f4e5e1fee0 ] 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: J. Bruce Fields <bfields@fieldses.org> Signed-off-by: Dai Ngo <dai.ngo@oracle.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
eb360efd38
commit
1657b2fb98
1 changed files with 101 additions and 8 deletions
|
@ -705,6 +705,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)
|
||||||
{
|
{
|
||||||
|
@ -4985,7 +5036,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;
|
||||||
|
@ -5001,6 +5052,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;
|
||||||
}
|
}
|
||||||
|
@ -5008,6 +5066,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;
|
||||||
}
|
}
|
||||||
|
@ -5054,21 +5119,29 @@ out_put_access:
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -5409,7 +5482,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);
|
||||||
|
@ -5643,6 +5716,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
|
||||||
*/
|
*/
|
||||||
|
@ -5650,8 +5743,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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue