aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/callback_proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/callback_proc.c')
-rw-r--r--fs/nfs/callback_proc.c165
1 files changed, 137 insertions, 28 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index defa9b4c470e..84761b5bb8e2 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -143,44 +143,49 @@ int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const n
143 * Return success if the sequenceID is one more than what we last saw on 143 * Return success if the sequenceID is one more than what we last saw on
144 * this slot, accounting for wraparound. Increments the slot's sequence. 144 * this slot, accounting for wraparound. Increments the slot's sequence.
145 * 145 *
146 * We don't yet implement a duplicate request cache, so at this time 146 * We don't yet implement a duplicate request cache, instead we set the
147 * we will log replays, and process them as if we had not seen them before, 147 * back channel ca_maxresponsesize_cached to zero. This is OK for now
148 * but we don't bump the sequence in the slot. Not too worried about it,
149 * since we only currently implement idempotent callbacks anyway. 148 * since we only currently implement idempotent callbacks anyway.
150 * 149 *
151 * We have a single slot backchannel at this time, so we don't bother 150 * We have a single slot backchannel at this time, so we don't bother
152 * checking the used_slots bit array on the table. The lower layer guarantees 151 * checking the used_slots bit array on the table. The lower layer guarantees
153 * a single outstanding callback request at a time. 152 * a single outstanding callback request at a time.
154 */ 153 */
155static int 154static __be32
156validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid) 155validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
157{ 156{
158 struct nfs4_slot *slot; 157 struct nfs4_slot *slot;
159 158
160 dprintk("%s enter. slotid %d seqid %d\n", 159 dprintk("%s enter. slotid %d seqid %d\n",
161 __func__, slotid, seqid); 160 __func__, args->csa_slotid, args->csa_sequenceid);
162 161
163 if (slotid > NFS41_BC_MAX_CALLBACKS) 162 if (args->csa_slotid > NFS41_BC_MAX_CALLBACKS)
164 return htonl(NFS4ERR_BADSLOT); 163 return htonl(NFS4ERR_BADSLOT);
165 164
166 slot = tbl->slots + slotid; 165 slot = tbl->slots + args->csa_slotid;
167 dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); 166 dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr);
168 167
169 /* Normal */ 168 /* Normal */
170 if (likely(seqid == slot->seq_nr + 1)) { 169 if (likely(args->csa_sequenceid == slot->seq_nr + 1)) {
171 slot->seq_nr++; 170 slot->seq_nr++;
172 return htonl(NFS4_OK); 171 return htonl(NFS4_OK);
173 } 172 }
174 173
175 /* Replay */ 174 /* Replay */
176 if (seqid == slot->seq_nr) { 175 if (args->csa_sequenceid == slot->seq_nr) {
177 dprintk("%s seqid %d is a replay - no DRC available\n", 176 dprintk("%s seqid %d is a replay\n",
178 __func__, seqid); 177 __func__, args->csa_sequenceid);
179 return htonl(NFS4_OK); 178 /* Signal process_op to set this error on next op */
179 if (args->csa_cachethis == 0)
180 return htonl(NFS4ERR_RETRY_UNCACHED_REP);
181
182 /* The ca_maxresponsesize_cached is 0 with no DRC */
183 else if (args->csa_cachethis == 1)
184 return htonl(NFS4ERR_REP_TOO_BIG_TO_CACHE);
180 } 185 }
181 186
182 /* Wraparound */ 187 /* Wraparound */
183 if (seqid == 1 && (slot->seq_nr + 1) == 0) { 188 if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) {
184 slot->seq_nr = 1; 189 slot->seq_nr = 1;
185 return htonl(NFS4_OK); 190 return htonl(NFS4_OK);
186 } 191 }
@@ -225,27 +230,87 @@ validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid)
225 return NULL; 230 return NULL;
226} 231}
227 232
228/* FIXME: referring calls should be processed */ 233/*
229unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, 234 * For each referring call triple, check the session's slot table for
235 * a match. If the slot is in use and the sequence numbers match, the
236 * client is still waiting for a response to the original request.
237 */
238static bool referring_call_exists(struct nfs_client *clp,
239 uint32_t nrclists,
240 struct referring_call_list *rclists)
241{
242 bool status = 0;
243 int i, j;
244 struct nfs4_session *session;
245 struct nfs4_slot_table *tbl;
246 struct referring_call_list *rclist;
247 struct referring_call *ref;
248
249 /*
250 * XXX When client trunking is implemented, this becomes
251 * a session lookup from within the loop
252 */
253 session = clp->cl_session;
254 tbl = &session->fc_slot_table;
255
256 for (i = 0; i < nrclists; i++) {
257 rclist = &rclists[i];
258 if (memcmp(session->sess_id.data,
259 rclist->rcl_sessionid.data,
260 NFS4_MAX_SESSIONID_LEN) != 0)
261 continue;
262
263 for (j = 0; j < rclist->rcl_nrefcalls; j++) {
264 ref = &rclist->rcl_refcalls[j];
265
266 dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u "
267 "slotid %u\n", __func__,
268 ((u32 *)&rclist->rcl_sessionid.data)[0],
269 ((u32 *)&rclist->rcl_sessionid.data)[1],
270 ((u32 *)&rclist->rcl_sessionid.data)[2],
271 ((u32 *)&rclist->rcl_sessionid.data)[3],
272 ref->rc_sequenceid, ref->rc_slotid);
273
274 spin_lock(&tbl->slot_tbl_lock);
275 status = (test_bit(ref->rc_slotid, tbl->used_slots) &&
276 tbl->slots[ref->rc_slotid].seq_nr ==
277 ref->rc_sequenceid);
278 spin_unlock(&tbl->slot_tbl_lock);
279 if (status)
280 goto out;
281 }
282 }
283
284out:
285 return status;
286}
287
288__be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
230 struct cb_sequenceres *res) 289 struct cb_sequenceres *res)
231{ 290{
232 struct nfs_client *clp; 291 struct nfs_client *clp;
233 int i, status; 292 int i;
234 293 __be32 status;
235 for (i = 0; i < args->csa_nrclists; i++)
236 kfree(args->csa_rclists[i].rcl_refcalls);
237 kfree(args->csa_rclists);
238 294
239 status = htonl(NFS4ERR_BADSESSION); 295 status = htonl(NFS4ERR_BADSESSION);
240 clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); 296 clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid);
241 if (clp == NULL) 297 if (clp == NULL)
242 goto out; 298 goto out;
243 299
244 status = validate_seqid(&clp->cl_session->bc_slot_table, 300 status = validate_seqid(&clp->cl_session->bc_slot_table, args);
245 args->csa_slotid, args->csa_sequenceid);
246 if (status) 301 if (status)
247 goto out_putclient; 302 goto out_putclient;
248 303
304 /*
305 * Check for pending referring calls. If a match is found, a
306 * related callback was received before the response to the original
307 * call.
308 */
309 if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
310 status = htonl(NFS4ERR_DELAY);
311 goto out_putclient;
312 }
313
249 memcpy(&res->csr_sessionid, &args->csa_sessionid, 314 memcpy(&res->csr_sessionid, &args->csa_sessionid,
250 sizeof(res->csr_sessionid)); 315 sizeof(res->csr_sessionid));
251 res->csr_sequenceid = args->csa_sequenceid; 316 res->csr_sequenceid = args->csa_sequenceid;
@@ -256,15 +321,23 @@ unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
256out_putclient: 321out_putclient:
257 nfs_put_client(clp); 322 nfs_put_client(clp);
258out: 323out:
259 dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 324 for (i = 0; i < args->csa_nrclists; i++)
260 res->csr_status = status; 325 kfree(args->csa_rclists[i].rcl_refcalls);
261 return res->csr_status; 326 kfree(args->csa_rclists);
327
328 if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP))
329 res->csr_status = 0;
330 else
331 res->csr_status = status;
332 dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
333 ntohl(status), ntohl(res->csr_status));
334 return status;
262} 335}
263 336
264unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy) 337__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
265{ 338{
266 struct nfs_client *clp; 339 struct nfs_client *clp;
267 int status; 340 __be32 status;
268 fmode_t flags = 0; 341 fmode_t flags = 0;
269 342
270 status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 343 status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
@@ -289,4 +362,40 @@ out:
289 dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 362 dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
290 return status; 363 return status;
291} 364}
365
366/* Reduce the fore channel's max_slots to the target value */
367__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy)
368{
369 struct nfs_client *clp;
370 struct nfs4_slot_table *fc_tbl;
371 __be32 status;
372
373 status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
374 clp = nfs_find_client(args->crsa_addr, 4);
375 if (clp == NULL)
376 goto out;
377
378 dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
379 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
380 args->crsa_target_max_slots);
381
382 fc_tbl = &clp->cl_session->fc_slot_table;
383
384 status = htonl(NFS4ERR_BAD_HIGH_SLOT);
385 if (args->crsa_target_max_slots > fc_tbl->max_slots ||
386 args->crsa_target_max_slots < 1)
387 goto out_putclient;
388
389 status = htonl(NFS4_OK);
390 if (args->crsa_target_max_slots == fc_tbl->max_slots)
391 goto out_putclient;
392
393 fc_tbl->target_max_slots = args->crsa_target_max_slots;
394 nfs41_handle_recall_slot(clp);
395out_putclient:
396 nfs_put_client(clp); /* balance nfs_find_client */
397out:
398 dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
399 return status;
400}
292#endif /* CONFIG_NFS_V4_1 */ 401#endif /* CONFIG_NFS_V4_1 */