aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2016-07-23 21:11:43 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2016-07-24 16:16:38 -0400
commit793b7fe55858dca1f5bd3e42185b541a9eddc144 (patch)
tree3a6a4d1b8a38613f62074800e7afeb46895cc630
parentecebb80bf3ee8c5f3172f00bb17ba55f9e3ae24f (diff)
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>
-rw-r--r--fs/nfs/callback_proc.c63
1 files changed, 44 insertions, 19 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index aaa2e8d3df6f..837da8a02d35 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -119,27 +119,30 @@ out:
119 * hashed by filehandle. 119 * hashed by filehandle.
120 */ 120 */
121static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp, 121static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
122 struct nfs_fh *fh, nfs4_stateid *stateid) 122 struct nfs_fh *fh)
123{ 123{
124 struct nfs_server *server; 124 struct nfs_server *server;
125 struct nfs_inode *nfsi;
125 struct inode *ino; 126 struct inode *ino;
126 struct pnfs_layout_hdr *lo; 127 struct pnfs_layout_hdr *lo;
127 128
129restart:
128 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 130 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
129 list_for_each_entry(lo, &server->layouts, plh_layouts) { 131 list_for_each_entry(lo, &server->layouts, plh_layouts) {
130 if (!nfs4_stateid_match_other(&lo->plh_stateid, stateid)) 132 nfsi = NFS_I(lo->plh_inode);
133 if (nfs_compare_fh(fh, &nfsi->fh))
131 continue; 134 continue;
132 if (nfs_compare_fh(fh, &NFS_I(lo->plh_inode)->fh)) 135 if (nfsi->layout != lo)
133 continue; 136 continue;
134 ino = igrab(lo->plh_inode); 137 ino = igrab(lo->plh_inode);
135 if (!ino) 138 if (!ino)
136 break; 139 break;
137 spin_lock(&ino->i_lock); 140 spin_lock(&ino->i_lock);
138 /* Is this layout in the process of being freed? */ 141 /* Is this layout in the process of being freed? */
139 if (NFS_I(ino)->layout != lo) { 142 if (nfsi->layout != lo) {
140 spin_unlock(&ino->i_lock); 143 spin_unlock(&ino->i_lock);
141 iput(ino); 144 iput(ino);
142 break; 145 goto restart;
143 } 146 }
144 pnfs_get_layout_hdr(lo); 147 pnfs_get_layout_hdr(lo);
145 spin_unlock(&ino->i_lock); 148 spin_unlock(&ino->i_lock);
@@ -151,13 +154,13 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
151} 154}
152 155
153static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp, 156static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
154 struct nfs_fh *fh, nfs4_stateid *stateid) 157 struct nfs_fh *fh)
155{ 158{
156 struct pnfs_layout_hdr *lo; 159 struct pnfs_layout_hdr *lo;
157 160
158 spin_lock(&clp->cl_lock); 161 spin_lock(&clp->cl_lock);
159 rcu_read_lock(); 162 rcu_read_lock();
160 lo = get_layout_by_fh_locked(clp, fh, stateid); 163 lo = get_layout_by_fh_locked(clp, fh);
161 rcu_read_unlock(); 164 rcu_read_unlock();
162 spin_unlock(&clp->cl_lock); 165 spin_unlock(&clp->cl_lock);
163 166
@@ -167,17 +170,39 @@ static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
167/* 170/*
168 * Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing) 171 * Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing)
169 */ 172 */
170static bool pnfs_check_stateid_sequence(struct pnfs_layout_hdr *lo, 173static u32 pnfs_check_callback_stateid(struct pnfs_layout_hdr *lo,
171 const nfs4_stateid *new) 174 const nfs4_stateid *new)
172{ 175{
173 u32 oldseq, newseq; 176 u32 oldseq, newseq;
174 177
175 oldseq = be32_to_cpu(lo->plh_stateid.seqid); 178 /* Is the stateid still not initialised? */
179 if (!pnfs_layout_is_valid(lo))
180 return NFS4ERR_DELAY;
181
182 /* Mismatched stateid? */
183 if (!nfs4_stateid_match_other(&lo->plh_stateid, new))
184 return NFS4ERR_BAD_STATEID;
185
176 newseq = be32_to_cpu(new->seqid); 186 newseq = be32_to_cpu(new->seqid);
187 /* Are we already in a layout recall situation? */
188 if (test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) &&
189 lo->plh_return_seq != 0) {
190 if (newseq < lo->plh_return_seq)
191 return NFS4ERR_OLD_STATEID;
192 if (newseq > lo->plh_return_seq)
193 return NFS4ERR_DELAY;
194 goto out;
195 }
177 196
197 /* Check that the stateid matches what we think it should be. */
198 oldseq = be32_to_cpu(lo->plh_stateid.seqid);
178 if (newseq > oldseq + 1) 199 if (newseq > oldseq + 1)
179 return false; 200 return NFS4ERR_DELAY;
180 return true; 201 /* Crazy server! */
202 if (newseq <= oldseq)
203 return NFS4ERR_OLD_STATEID;
204out:
205 return NFS_OK;
181} 206}
182 207
183static u32 initiate_file_draining(struct nfs_client *clp, 208static u32 initiate_file_draining(struct nfs_client *clp,
@@ -188,7 +213,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
188 u32 rv = NFS4ERR_NOMATCHING_LAYOUT; 213 u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
189 LIST_HEAD(free_me_list); 214 LIST_HEAD(free_me_list);
190 215
191 lo = get_layout_by_fh(clp, &args->cbl_fh, &args->cbl_stateid); 216 lo = get_layout_by_fh(clp, &args->cbl_fh);
192 if (!lo) { 217 if (!lo) {
193 trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL, 218 trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
194 &args->cbl_stateid, -rv); 219 &args->cbl_stateid, -rv);
@@ -196,18 +221,15 @@ static u32 initiate_file_draining(struct nfs_client *clp,
196 } 221 }
197 222
198 ino = lo->plh_inode; 223 ino = lo->plh_inode;
224 pnfs_layoutcommit_inode(ino, false);
225
199 226
200 spin_lock(&ino->i_lock); 227 spin_lock(&ino->i_lock);
201 if (!pnfs_check_stateid_sequence(lo, &args->cbl_stateid)) { 228 rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
202 rv = NFS4ERR_DELAY; 229 if (rv != NFS_OK)
203 goto unlock; 230 goto unlock;
204 }
205 pnfs_set_layout_stateid(lo, &args->cbl_stateid, true); 231 pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
206 spin_unlock(&ino->i_lock);
207
208 pnfs_layoutcommit_inode(ino, false);
209 232
210 spin_lock(&ino->i_lock);
211 /* 233 /*
212 * Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return) 234 * Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
213 */ 235 */
@@ -223,6 +245,9 @@ static u32 initiate_file_draining(struct nfs_client *clp,
223 goto unlock; 245 goto unlock;
224 } 246 }
225 247
248 /* Embrace your forgetfulness! */
249 rv = NFS4ERR_NOMATCHING_LAYOUT;
250
226 if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) { 251 if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
227 NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, 252 NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
228 &args->cbl_range); 253 &args->cbl_range);