mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-25 13:43:55 +08:00
pNFS: Fix CB_LAYOUTRECALL stateid verification
We want to evaluate in this order: If the client holds no layout for this inode, then return NFS4ERR_NOMATCHING_LAYOUT; it probably forgot the layout. If the client finds the inode among the list of layouts, but the corresponding stateid has not yet been initialised, then return NFS4ERR_DELAY to ask the server to retry once the outstanding LAYOUTGET is complete. If the current layout stateid's "other" field does not match the recalled stateid, return NFS4ERR_BAD_STATEID. If already processing a layout recall with a newer stateid, return NFS4ERR_OLD_STATEID. This can only happens for servers that are non-compliant with the NFSv4.1 protocol. If already processing a layout recall with an older stateid, return NFS4ERR_DELAY to ask the server to retry once the outstanding LAYOUTRETURN is complete. Again, this is technically incompliant with the NFSv4.1 protocol. If the current layout sequence id is newer than the recalled stateid's sequence id, return NFS4ERR_OLD_STATEID. This too implies protocol non-compliance. If the current layout sequence id is older than the recalled stateid's sequence id+1, return NFS4ERR_DELAY. Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
This commit is contained in:
parent
ecebb80bf3
commit
793b7fe558
@ -119,27 +119,30 @@ out:
|
|||||||
* hashed by filehandle.
|
* hashed by filehandle.
|
||||||
*/
|
*/
|
||||||
static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
|
static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
|
||||||
struct nfs_fh *fh, nfs4_stateid *stateid)
|
struct nfs_fh *fh)
|
||||||
{
|
{
|
||||||
struct nfs_server *server;
|
struct nfs_server *server;
|
||||||
|
struct nfs_inode *nfsi;
|
||||||
struct inode *ino;
|
struct inode *ino;
|
||||||
struct pnfs_layout_hdr *lo;
|
struct pnfs_layout_hdr *lo;
|
||||||
|
|
||||||
|
restart:
|
||||||
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
||||||
list_for_each_entry(lo, &server->layouts, plh_layouts) {
|
list_for_each_entry(lo, &server->layouts, plh_layouts) {
|
||||||
if (!nfs4_stateid_match_other(&lo->plh_stateid, stateid))
|
nfsi = NFS_I(lo->plh_inode);
|
||||||
|
if (nfs_compare_fh(fh, &nfsi->fh))
|
||||||
continue;
|
continue;
|
||||||
if (nfs_compare_fh(fh, &NFS_I(lo->plh_inode)->fh))
|
if (nfsi->layout != lo)
|
||||||
continue;
|
continue;
|
||||||
ino = igrab(lo->plh_inode);
|
ino = igrab(lo->plh_inode);
|
||||||
if (!ino)
|
if (!ino)
|
||||||
break;
|
break;
|
||||||
spin_lock(&ino->i_lock);
|
spin_lock(&ino->i_lock);
|
||||||
/* Is this layout in the process of being freed? */
|
/* Is this layout in the process of being freed? */
|
||||||
if (NFS_I(ino)->layout != lo) {
|
if (nfsi->layout != lo) {
|
||||||
spin_unlock(&ino->i_lock);
|
spin_unlock(&ino->i_lock);
|
||||||
iput(ino);
|
iput(ino);
|
||||||
break;
|
goto restart;
|
||||||
}
|
}
|
||||||
pnfs_get_layout_hdr(lo);
|
pnfs_get_layout_hdr(lo);
|
||||||
spin_unlock(&ino->i_lock);
|
spin_unlock(&ino->i_lock);
|
||||||
@ -151,13 +154,13 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
|
static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
|
||||||
struct nfs_fh *fh, nfs4_stateid *stateid)
|
struct nfs_fh *fh)
|
||||||
{
|
{
|
||||||
struct pnfs_layout_hdr *lo;
|
struct pnfs_layout_hdr *lo;
|
||||||
|
|
||||||
spin_lock(&clp->cl_lock);
|
spin_lock(&clp->cl_lock);
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
lo = get_layout_by_fh_locked(clp, fh, stateid);
|
lo = get_layout_by_fh_locked(clp, fh);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
spin_unlock(&clp->cl_lock);
|
spin_unlock(&clp->cl_lock);
|
||||||
|
|
||||||
@ -167,17 +170,39 @@ static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
|
|||||||
/*
|
/*
|
||||||
* Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing)
|
* Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing)
|
||||||
*/
|
*/
|
||||||
static bool pnfs_check_stateid_sequence(struct pnfs_layout_hdr *lo,
|
static u32 pnfs_check_callback_stateid(struct pnfs_layout_hdr *lo,
|
||||||
const nfs4_stateid *new)
|
const nfs4_stateid *new)
|
||||||
{
|
{
|
||||||
u32 oldseq, newseq;
|
u32 oldseq, newseq;
|
||||||
|
|
||||||
oldseq = be32_to_cpu(lo->plh_stateid.seqid);
|
/* Is the stateid still not initialised? */
|
||||||
newseq = be32_to_cpu(new->seqid);
|
if (!pnfs_layout_is_valid(lo))
|
||||||
|
return NFS4ERR_DELAY;
|
||||||
|
|
||||||
|
/* Mismatched stateid? */
|
||||||
|
if (!nfs4_stateid_match_other(&lo->plh_stateid, new))
|
||||||
|
return NFS4ERR_BAD_STATEID;
|
||||||
|
|
||||||
|
newseq = be32_to_cpu(new->seqid);
|
||||||
|
/* Are we already in a layout recall situation? */
|
||||||
|
if (test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) &&
|
||||||
|
lo->plh_return_seq != 0) {
|
||||||
|
if (newseq < lo->plh_return_seq)
|
||||||
|
return NFS4ERR_OLD_STATEID;
|
||||||
|
if (newseq > lo->plh_return_seq)
|
||||||
|
return NFS4ERR_DELAY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the stateid matches what we think it should be. */
|
||||||
|
oldseq = be32_to_cpu(lo->plh_stateid.seqid);
|
||||||
if (newseq > oldseq + 1)
|
if (newseq > oldseq + 1)
|
||||||
return false;
|
return NFS4ERR_DELAY;
|
||||||
return true;
|
/* Crazy server! */
|
||||||
|
if (newseq <= oldseq)
|
||||||
|
return NFS4ERR_OLD_STATEID;
|
||||||
|
out:
|
||||||
|
return NFS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 initiate_file_draining(struct nfs_client *clp,
|
static u32 initiate_file_draining(struct nfs_client *clp,
|
||||||
@ -188,7 +213,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
|
|||||||
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
|
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
|
||||||
LIST_HEAD(free_me_list);
|
LIST_HEAD(free_me_list);
|
||||||
|
|
||||||
lo = get_layout_by_fh(clp, &args->cbl_fh, &args->cbl_stateid);
|
lo = get_layout_by_fh(clp, &args->cbl_fh);
|
||||||
if (!lo) {
|
if (!lo) {
|
||||||
trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
|
trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
|
||||||
&args->cbl_stateid, -rv);
|
&args->cbl_stateid, -rv);
|
||||||
@ -196,18 +221,15 @@ static u32 initiate_file_draining(struct nfs_client *clp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ino = lo->plh_inode;
|
ino = lo->plh_inode;
|
||||||
|
|
||||||
spin_lock(&ino->i_lock);
|
|
||||||
if (!pnfs_check_stateid_sequence(lo, &args->cbl_stateid)) {
|
|
||||||
rv = NFS4ERR_DELAY;
|
|
||||||
goto unlock;
|
|
||||||
}
|
|
||||||
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
|
|
||||||
spin_unlock(&ino->i_lock);
|
|
||||||
|
|
||||||
pnfs_layoutcommit_inode(ino, false);
|
pnfs_layoutcommit_inode(ino, false);
|
||||||
|
|
||||||
|
|
||||||
spin_lock(&ino->i_lock);
|
spin_lock(&ino->i_lock);
|
||||||
|
rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
|
||||||
|
if (rv != NFS_OK)
|
||||||
|
goto unlock;
|
||||||
|
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
|
* Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
|
||||||
*/
|
*/
|
||||||
@ -223,6 +245,9 @@ static u32 initiate_file_draining(struct nfs_client *clp,
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Embrace your forgetfulness! */
|
||||||
|
rv = NFS4ERR_NOMATCHING_LAYOUT;
|
||||||
|
|
||||||
if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
|
if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
|
||||||
NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
|
NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
|
||||||
&args->cbl_range);
|
&args->cbl_range);
|
||||||
|
Loading…
Reference in New Issue
Block a user