@@ -374,6 +374,19 @@ encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
374
374
hdr -> nops ++ ;
375
375
}
376
376
377
+ static u32 highest_slotid (struct nfsd4_session * ses )
378
+ {
379
+ u32 idx ;
380
+
381
+ spin_lock (& ses -> se_lock );
382
+ idx = fls (~ses -> se_cb_slot_avail );
383
+ if (idx > 0 )
384
+ -- idx ;
385
+ idx = max (idx , ses -> se_cb_highest_slot );
386
+ spin_unlock (& ses -> se_lock );
387
+ return idx ;
388
+ }
389
+
377
390
/*
378
391
* CB_SEQUENCE4args
379
392
*
@@ -400,15 +413,40 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
400
413
encode_sessionid4 (xdr , session );
401
414
402
415
p = xdr_reserve_space (xdr , 4 + 4 + 4 + 4 + 4 );
403
- * p ++ = cpu_to_be32 (session -> se_cb_seq_nr ); /* csa_sequenceid */
404
- * p ++ = xdr_zero ; /* csa_slotid */
405
- * p ++ = xdr_zero ; /* csa_highest_slotid */
416
+ * p ++ = cpu_to_be32 (session -> se_cb_seq_nr [ cb -> cb_held_slot ] ); /* csa_sequenceid */
417
+ * p ++ = cpu_to_be32 ( cb -> cb_held_slot ); /* csa_slotid */
418
+ * p ++ = cpu_to_be32 ( highest_slotid ( session )); /* csa_highest_slotid */
406
419
* p ++ = xdr_zero ; /* csa_cachethis */
407
420
xdr_encode_empty_array (p ); /* csa_referring_call_lists */
408
421
409
422
hdr -> nops ++ ;
410
423
}
411
424
425
+ static void update_cb_slot_table (struct nfsd4_session * ses , u32 target )
426
+ {
427
+ /* No need to do anything if nothing changed */
428
+ if (likely (target == READ_ONCE (ses -> se_cb_highest_slot )))
429
+ return ;
430
+
431
+ spin_lock (& ses -> se_lock );
432
+ if (target > ses -> se_cb_highest_slot ) {
433
+ int i ;
434
+
435
+ target = min (target , NFSD_BC_SLOT_TABLE_SIZE - 1 );
436
+
437
+ /*
438
+ * Growing the slot table. Reset any new sequences to 1.
439
+ *
440
+ * NB: There is some debate about whether the RFC requires this,
441
+ * but the Linux client expects it.
442
+ */
443
+ for (i = ses -> se_cb_highest_slot + 1 ; i <= target ; ++ i )
444
+ ses -> se_cb_seq_nr [i ] = 1 ;
445
+ }
446
+ ses -> se_cb_highest_slot = target ;
447
+ spin_unlock (& ses -> se_lock );
448
+ }
449
+
412
450
/*
413
451
* CB_SEQUENCE4resok
414
452
*
@@ -436,7 +474,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
436
474
struct nfsd4_session * session = cb -> cb_clp -> cl_cb_session ;
437
475
int status = - ESERVERFAULT ;
438
476
__be32 * p ;
439
- u32 dummy ;
477
+ u32 seqid , slotid , target ;
440
478
441
479
/*
442
480
* If the server returns different values for sessionID, slotID or
@@ -452,21 +490,22 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
452
490
}
453
491
p += XDR_QUADLEN (NFS4_MAX_SESSIONID_LEN );
454
492
455
- dummy = be32_to_cpup (p ++ );
456
- if (dummy != session -> se_cb_seq_nr ) {
493
+ seqid = be32_to_cpup (p ++ );
494
+ if (seqid != session -> se_cb_seq_nr [ cb -> cb_held_slot ] ) {
457
495
dprintk ("NFS: %s Invalid sequence number\n" , __func__ );
458
496
goto out ;
459
497
}
460
498
461
- dummy = be32_to_cpup (p ++ );
462
- if (dummy != 0 ) {
499
+ slotid = be32_to_cpup (p ++ );
500
+ if (slotid != cb -> cb_held_slot ) {
463
501
dprintk ("NFS: %s Invalid slotid\n" , __func__ );
464
502
goto out ;
465
503
}
466
504
467
- /*
468
- * FIXME: process highest slotid and target highest slotid
469
- */
505
+ p ++ ; // ignore current highest slot value
506
+
507
+ target = be32_to_cpup (p ++ );
508
+ update_cb_slot_table (session , target );
470
509
status = 0 ;
471
510
out :
472
511
cb -> cb_seq_status = status ;
@@ -1167,6 +1206,22 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
1167
1206
spin_unlock (& clp -> cl_lock );
1168
1207
}
1169
1208
1209
+ static int grab_slot (struct nfsd4_session * ses )
1210
+ {
1211
+ int idx ;
1212
+
1213
+ spin_lock (& ses -> se_lock );
1214
+ idx = ffs (ses -> se_cb_slot_avail ) - 1 ;
1215
+ if (idx < 0 || idx > ses -> se_cb_highest_slot ) {
1216
+ spin_unlock (& ses -> se_lock );
1217
+ return -1 ;
1218
+ }
1219
+ /* clear the bit for the slot */
1220
+ ses -> se_cb_slot_avail &= ~BIT (idx );
1221
+ spin_unlock (& ses -> se_lock );
1222
+ return idx ;
1223
+ }
1224
+
1170
1225
/*
1171
1226
* There's currently a single callback channel slot.
1172
1227
* If the slot is available, then mark it busy. Otherwise, set the
@@ -1175,28 +1230,32 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
1175
1230
static bool nfsd41_cb_get_slot (struct nfsd4_callback * cb , struct rpc_task * task )
1176
1231
{
1177
1232
struct nfs4_client * clp = cb -> cb_clp ;
1233
+ struct nfsd4_session * ses = clp -> cl_cb_session ;
1178
1234
1179
- if (!cb -> cb_holds_slot &&
1180
- test_and_set_bit (0 , & clp -> cl_cb_slot_busy ) != 0 ) {
1235
+ if (cb -> cb_held_slot >= 0 )
1236
+ return true;
1237
+ cb -> cb_held_slot = grab_slot (ses );
1238
+ if (cb -> cb_held_slot < 0 ) {
1181
1239
rpc_sleep_on (& clp -> cl_cb_waitq , task , NULL );
1182
1240
/* Race breaker */
1183
- if ( test_and_set_bit ( 0 , & clp -> cl_cb_slot_busy ) != 0 ) {
1184
- dprintk ( "%s slot is busy\n" , __func__ );
1241
+ cb -> cb_held_slot = grab_slot ( ses );
1242
+ if ( cb -> cb_held_slot < 0 )
1185
1243
return false;
1186
- }
1187
1244
rpc_wake_up_queued_task (& clp -> cl_cb_waitq , task );
1188
1245
}
1189
- cb -> cb_holds_slot = true;
1190
1246
return true;
1191
1247
}
1192
1248
1193
1249
static void nfsd41_cb_release_slot (struct nfsd4_callback * cb )
1194
1250
{
1195
1251
struct nfs4_client * clp = cb -> cb_clp ;
1252
+ struct nfsd4_session * ses = clp -> cl_cb_session ;
1196
1253
1197
- if (cb -> cb_holds_slot ) {
1198
- cb -> cb_holds_slot = false;
1199
- clear_bit (0 , & clp -> cl_cb_slot_busy );
1254
+ if (cb -> cb_held_slot >= 0 ) {
1255
+ spin_lock (& ses -> se_lock );
1256
+ ses -> se_cb_slot_avail |= BIT (cb -> cb_held_slot );
1257
+ spin_unlock (& ses -> se_lock );
1258
+ cb -> cb_held_slot = -1 ;
1200
1259
rpc_wake_up_next (& clp -> cl_cb_waitq );
1201
1260
}
1202
1261
}
@@ -1213,8 +1272,8 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
1213
1272
}
1214
1273
1215
1274
/*
1216
- * TODO: cb_sequence should support referring call lists, cachethis, multiple
1217
- * slots, and mark callback channel down on communication errors.
1275
+ * TODO: cb_sequence should support referring call lists, cachethis,
1276
+ * and mark callback channel down on communication errors.
1218
1277
*/
1219
1278
static void nfsd4_cb_prepare (struct rpc_task * task , void * calldata )
1220
1279
{
@@ -1256,7 +1315,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
1256
1315
return true;
1257
1316
}
1258
1317
1259
- if (! cb -> cb_holds_slot )
1318
+ if (cb -> cb_held_slot < 0 )
1260
1319
goto need_restart ;
1261
1320
1262
1321
/* This is the operation status code for CB_SEQUENCE */
@@ -1270,10 +1329,10 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
1270
1329
* If CB_SEQUENCE returns an error, then the state of the slot
1271
1330
* (sequence ID, cached reply) MUST NOT change.
1272
1331
*/
1273
- ++ session -> se_cb_seq_nr ;
1332
+ ++ session -> se_cb_seq_nr [ cb -> cb_held_slot ] ;
1274
1333
break ;
1275
1334
case - ESERVERFAULT :
1276
- ++ session -> se_cb_seq_nr ;
1335
+ ++ session -> se_cb_seq_nr [ cb -> cb_held_slot ] ;
1277
1336
nfsd4_mark_cb_fault (cb -> cb_clp );
1278
1337
ret = false;
1279
1338
break ;
@@ -1299,17 +1358,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
1299
1358
case - NFS4ERR_BADSLOT :
1300
1359
goto retry_nowait ;
1301
1360
case - NFS4ERR_SEQ_MISORDERED :
1302
- if (session -> se_cb_seq_nr != 1 ) {
1303
- session -> se_cb_seq_nr = 1 ;
1361
+ if (session -> se_cb_seq_nr [ cb -> cb_held_slot ] != 1 ) {
1362
+ session -> se_cb_seq_nr [ cb -> cb_held_slot ] = 1 ;
1304
1363
goto retry_nowait ;
1305
1364
}
1306
1365
break ;
1307
1366
default :
1308
1367
nfsd4_mark_cb_fault (cb -> cb_clp );
1309
1368
}
1310
- nfsd41_cb_release_slot (cb );
1311
-
1312
1369
trace_nfsd_cb_free_slot (task , cb );
1370
+ nfsd41_cb_release_slot (cb );
1313
1371
1314
1372
if (RPC_SIGNALLED (task ))
1315
1373
goto need_restart ;
@@ -1529,7 +1587,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
1529
1587
INIT_WORK (& cb -> cb_work , nfsd4_run_cb_work );
1530
1588
cb -> cb_status = 0 ;
1531
1589
cb -> cb_need_restart = false;
1532
- cb -> cb_holds_slot = false ;
1590
+ cb -> cb_held_slot = -1 ;
1533
1591
}
1534
1592
1535
1593
/**
0 commit comments