@@ -374,6 +374,19 @@ encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
374374 hdr -> nops ++ ;
375375}
376376
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+
377390/*
378391 * CB_SEQUENCE4args
379392 *
@@ -400,15 +413,40 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
400413 encode_sessionid4 (xdr , session );
401414
402415 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 */
406419 * p ++ = xdr_zero ; /* csa_cachethis */
407420 xdr_encode_empty_array (p ); /* csa_referring_call_lists */
408421
409422 hdr -> nops ++ ;
410423}
411424
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+
412450/*
413451 * CB_SEQUENCE4resok
414452 *
@@ -436,7 +474,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
436474 struct nfsd4_session * session = cb -> cb_clp -> cl_cb_session ;
437475 int status = - ESERVERFAULT ;
438476 __be32 * p ;
439- u32 dummy ;
477+ u32 seqid , slotid , target ;
440478
441479 /*
442480 * If the server returns different values for sessionID, slotID or
@@ -452,21 +490,22 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
452490 }
453491 p += XDR_QUADLEN (NFS4_MAX_SESSIONID_LEN );
454492
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 ] ) {
457495 dprintk ("NFS: %s Invalid sequence number\n" , __func__ );
458496 goto out ;
459497 }
460498
461- dummy = be32_to_cpup (p ++ );
462- if (dummy != 0 ) {
499+ slotid = be32_to_cpup (p ++ );
500+ if (slotid != cb -> cb_held_slot ) {
463501 dprintk ("NFS: %s Invalid slotid\n" , __func__ );
464502 goto out ;
465503 }
466504
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 );
470509 status = 0 ;
471510out :
472511 cb -> cb_seq_status = status ;
@@ -1167,6 +1206,22 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
11671206 spin_unlock (& clp -> cl_lock );
11681207}
11691208
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+
11701225/*
11711226 * There's currently a single callback channel slot.
11721227 * 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)
11751230static bool nfsd41_cb_get_slot (struct nfsd4_callback * cb , struct rpc_task * task )
11761231{
11771232 struct nfs4_client * clp = cb -> cb_clp ;
1233+ struct nfsd4_session * ses = clp -> cl_cb_session ;
11781234
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 ) {
11811239 rpc_sleep_on (& clp -> cl_cb_waitq , task , NULL );
11821240 /* 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 )
11851243 return false;
1186- }
11871244 rpc_wake_up_queued_task (& clp -> cl_cb_waitq , task );
11881245 }
1189- cb -> cb_holds_slot = true;
11901246 return true;
11911247}
11921248
11931249static void nfsd41_cb_release_slot (struct nfsd4_callback * cb )
11941250{
11951251 struct nfs4_client * clp = cb -> cb_clp ;
1252+ struct nfsd4_session * ses = clp -> cl_cb_session ;
11961253
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 ;
12001259 rpc_wake_up_next (& clp -> cl_cb_waitq );
12011260 }
12021261}
@@ -1213,8 +1272,8 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
12131272}
12141273
12151274/*
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.
12181277 */
12191278static void nfsd4_cb_prepare (struct rpc_task * task , void * calldata )
12201279{
@@ -1256,7 +1315,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
12561315 return true;
12571316 }
12581317
1259- if (! cb -> cb_holds_slot )
1318+ if (cb -> cb_held_slot < 0 )
12601319 goto need_restart ;
12611320
12621321 /* 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
12701329 * If CB_SEQUENCE returns an error, then the state of the slot
12711330 * (sequence ID, cached reply) MUST NOT change.
12721331 */
1273- ++ session -> se_cb_seq_nr ;
1332+ ++ session -> se_cb_seq_nr [ cb -> cb_held_slot ] ;
12741333 break ;
12751334 case - ESERVERFAULT :
1276- ++ session -> se_cb_seq_nr ;
1335+ ++ session -> se_cb_seq_nr [ cb -> cb_held_slot ] ;
12771336 nfsd4_mark_cb_fault (cb -> cb_clp );
12781337 ret = false;
12791338 break ;
@@ -1299,17 +1358,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
12991358 case - NFS4ERR_BADSLOT :
13001359 goto retry_nowait ;
13011360 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 ;
13041363 goto retry_nowait ;
13051364 }
13061365 break ;
13071366 default :
13081367 nfsd4_mark_cb_fault (cb -> cb_clp );
13091368 }
1310- nfsd41_cb_release_slot (cb );
1311-
13121369 trace_nfsd_cb_free_slot (task , cb );
1370+ nfsd41_cb_release_slot (cb );
13131371
13141372 if (RPC_SIGNALLED (task ))
13151373 goto need_restart ;
@@ -1529,7 +1587,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
15291587 INIT_WORK (& cb -> cb_work , nfsd4_run_cb_work );
15301588 cb -> cb_status = 0 ;
15311589 cb -> cb_need_restart = false;
1532- cb -> cb_holds_slot = false ;
1590+ cb -> cb_held_slot = -1 ;
15331591}
15341592
15351593/**
0 commit comments