Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nfsd: add support for freeing unused session-DRC slots

Reducing the number of slots in the session slot table requires
confirmation from the client. This patch adds reduce_session_slots()
which starts the process of getting confirmation, but never calls it.
That will come in a later patch.

Before we can free a slot we need to confirm that the client won't try
to use it again. This involves returning a lower cr_maxrequests in a
SEQUENCE reply and then seeing a ca_maxrequests on the same slot which
is not larger than we limit we are trying to impose. So for each slot
we need to remember that we have sent a reduced cr_maxrequests.

To achieve this we introduce a concept of request "generations". Each
time we decide to reduce cr_maxrequests we increment the generation
number, and record this when we return the lower cr_maxrequests to the
client. When a slot with the current generation reports a low
ca_maxrequests, we commit to that level and free extra slots.

We use an 16 bit generation number (64 seems wasteful) and if it cycles
we iterate all slots and reset the generation number to avoid false matches.

When we free a slot we store the seqid in the slot pointer so that it can
be restored when we reactivate the slot. The RFC can be read as
suggesting that the slot number could restart from one after a slot is
retired and reactivated, but also suggests that retiring slots is not
required. So when we reactive a slot we accept with the next seqid in
sequence, or 1.

When decoding sa_highest_slotid into maxslots we need to add 1 - this
matches how it is encoded for the reply.

se_dead is moved in struct nfsd4_session to remove a hole.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by

NeilBrown and committed by
Chuck Lever
fc8738c6 60aa6564

+92 -15
+84 -10
fs/nfsd/nfs4state.c
··· 1910 1910 #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44) 1911 1911 1912 1912 static void 1913 - free_session_slots(struct nfsd4_session *ses) 1913 + free_session_slots(struct nfsd4_session *ses, int from) 1914 1914 { 1915 1915 int i; 1916 1916 1917 - for (i = 0; i < ses->se_fchannel.maxreqs; i++) { 1917 + if (from >= ses->se_fchannel.maxreqs) 1918 + return; 1919 + 1920 + for (i = from; i < ses->se_fchannel.maxreqs; i++) { 1918 1921 struct nfsd4_slot *slot = xa_load(&ses->se_slots, i); 1919 1922 1920 - xa_erase(&ses->se_slots, i); 1923 + /* 1924 + * Save the seqid in case we reactivate this slot. 1925 + * This will never require a memory allocation so GFP 1926 + * flag is irrelevant 1927 + */ 1928 + xa_store(&ses->se_slots, i, xa_mk_value(slot->sl_seqid), 0); 1921 1929 free_svc_cred(&slot->sl_cred); 1922 1930 kfree(slot); 1923 1931 } 1932 + ses->se_fchannel.maxreqs = from; 1933 + if (ses->se_target_maxslots > from) 1934 + ses->se_target_maxslots = from; 1935 + } 1936 + 1937 + /** 1938 + * reduce_session_slots - reduce the target max-slots of a session if possible 1939 + * @ses: The session to affect 1940 + * @dec: how much to decrease the target by 1941 + * 1942 + * This interface can be used by a shrinker to reduce the target max-slots 1943 + * for a session so that some slots can eventually be freed. 1944 + * It uses spin_trylock() as it may be called in a context where another 1945 + * spinlock is held that has a dependency on client_lock. As shrinkers are 1946 + * best-effort, skiping a session is client_lock is already held has no 1947 + * great coast 1948 + * 1949 + * Return value: 1950 + * The number of slots that the target was reduced by. 1951 + */ 1952 + static int __maybe_unused 1953 + reduce_session_slots(struct nfsd4_session *ses, int dec) 1954 + { 1955 + struct nfsd_net *nn = net_generic(ses->se_client->net, 1956 + nfsd_net_id); 1957 + int ret = 0; 1958 + 1959 + if (ses->se_target_maxslots <= 1) 1960 + return ret; 1961 + if (!spin_trylock(&nn->client_lock)) 1962 + return ret; 1963 + ret = min(dec, ses->se_target_maxslots-1); 1964 + ses->se_target_maxslots -= ret; 1965 + ses->se_slot_gen += 1; 1966 + if (ses->se_slot_gen == 0) { 1967 + int i; 1968 + ses->se_slot_gen = 1; 1969 + for (i = 0; i < ses->se_fchannel.maxreqs; i++) { 1970 + struct nfsd4_slot *slot = xa_load(&ses->se_slots, i); 1971 + slot->sl_generation = 0; 1972 + } 1973 + } 1974 + spin_unlock(&nn->client_lock); 1975 + return ret; 1924 1976 } 1925 1977 1926 1978 /* ··· 2020 1968 } 2021 1969 fattrs->maxreqs = i; 2022 1970 memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs)); 1971 + new->se_target_maxslots = i; 2023 1972 new->se_cb_slot_avail = ~0U; 2024 1973 new->se_cb_highest_slot = min(battrs->maxreqs - 1, 2025 1974 NFSD_BC_SLOT_TABLE_SIZE - 1); ··· 2134 2081 2135 2082 static void __free_session(struct nfsd4_session *ses) 2136 2083 { 2137 - free_session_slots(ses); 2084 + free_session_slots(ses, 0); 2138 2085 xa_destroy(&ses->se_slots); 2139 2086 kfree(ses); 2140 2087 } ··· 2737 2684 seq_printf(m, "session slots:"); 2738 2685 list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) 2739 2686 seq_printf(m, " %u", ses->se_fchannel.maxreqs); 2687 + seq_printf(m, "\nsession target slots:"); 2688 + list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) 2689 + seq_printf(m, " %u", ses->se_target_maxslots); 2740 2690 spin_unlock(&clp->cl_lock); 2741 2691 seq_puts(m, "\n"); 2742 2692 ··· 3730 3674 kfree(exid->server_impl_name); 3731 3675 } 3732 3676 3733 - static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse) 3677 + static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, u8 flags) 3734 3678 { 3735 3679 /* The slot is in use, and no response has been sent. */ 3736 - if (slot_inuse) { 3680 + if (flags & NFSD4_SLOT_INUSE) { 3737 3681 if (seqid == slot_seqid) 3738 3682 return nfserr_jukebox; 3739 3683 else ··· 3741 3685 } 3742 3686 /* Note unsigned 32-bit arithmetic handles wraparound: */ 3743 3687 if (likely(seqid == slot_seqid + 1)) 3688 + return nfs_ok; 3689 + if ((flags & NFSD4_SLOT_REUSED) && seqid == 1) 3744 3690 return nfs_ok; 3745 3691 if (seqid == slot_seqid) 3746 3692 return nfserr_replay_cache; ··· 4294 4236 dprintk("%s: slotid %d\n", __func__, seq->slotid); 4295 4237 4296 4238 trace_nfsd_slot_seqid_sequence(clp, seq, slot); 4297 - status = check_slot_seqid(seq->seqid, slot->sl_seqid, 4298 - slot->sl_flags & NFSD4_SLOT_INUSE); 4239 + status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_flags); 4299 4240 if (status == nfserr_replay_cache) { 4300 4241 status = nfserr_seq_misordered; 4301 4242 if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED)) ··· 4319 4262 if (status) 4320 4263 goto out_put_session; 4321 4264 4265 + if (session->se_target_maxslots < session->se_fchannel.maxreqs && 4266 + slot->sl_generation == session->se_slot_gen && 4267 + seq->maxslots <= session->se_target_maxslots) 4268 + /* Client acknowledged our reduce maxreqs */ 4269 + free_session_slots(session, session->se_target_maxslots); 4270 + 4322 4271 buflen = (seq->cachethis) ? 4323 4272 session->se_fchannel.maxresp_cached : 4324 4273 session->se_fchannel.maxresp_sz; ··· 4335 4272 svc_reserve(rqstp, buflen); 4336 4273 4337 4274 status = nfs_ok; 4338 - /* Success! bump slot seqid */ 4275 + /* Success! accept new slot seqid */ 4339 4276 slot->sl_seqid = seq->seqid; 4277 + slot->sl_flags &= ~NFSD4_SLOT_REUSED; 4340 4278 slot->sl_flags |= NFSD4_SLOT_INUSE; 4279 + slot->sl_generation = session->se_slot_gen; 4341 4280 if (seq->cachethis) 4342 4281 slot->sl_flags |= NFSD4_SLOT_CACHETHIS; 4343 4282 else ··· 4356 4291 * the client might use. 4357 4292 */ 4358 4293 if (seq->slotid == session->se_fchannel.maxreqs - 1 && 4294 + session->se_target_maxslots >= session->se_fchannel.maxreqs && 4359 4295 session->se_fchannel.maxreqs < NFSD_MAX_SLOTS_PER_SESSION) { 4360 4296 int s = session->se_fchannel.maxreqs; 4361 4297 int cnt = DIV_ROUND_UP(s, 5); 4298 + void *prev_slot; 4362 4299 4363 4300 do { 4364 4301 /* ··· 4370 4303 */ 4371 4304 slot = kzalloc(slot_bytes(&session->se_fchannel), 4372 4305 GFP_NOWAIT); 4306 + prev_slot = xa_load(&session->se_slots, s); 4307 + if (xa_is_value(prev_slot) && slot) { 4308 + slot->sl_seqid = xa_to_value(prev_slot); 4309 + slot->sl_flags |= NFSD4_SLOT_REUSED; 4310 + } 4373 4311 if (slot && 4374 4312 !xa_is_err(xa_store(&session->se_slots, s, slot, 4375 4313 GFP_NOWAIT))) { 4376 4314 s += 1; 4377 4315 session->se_fchannel.maxreqs = s; 4316 + session->se_target_maxslots = s; 4378 4317 } else { 4379 4318 kfree(slot); 4380 4319 slot = NULL; 4381 4320 } 4382 4321 } while (slot && --cnt > 0); 4383 4322 } 4384 - seq->maxslots = session->se_fchannel.maxreqs; 4323 + seq->maxslots = max(session->se_target_maxslots, seq->maxslots); 4324 + seq->target_maxslots = session->se_target_maxslots; 4385 4325 4386 4326 out: 4387 4327 switch (clp->cl_cb_state) {
+3 -2
fs/nfsd/nfs4xdr.c
··· 1884 1884 return nfserr_bad_xdr; 1885 1885 seq->seqid = be32_to_cpup(p++); 1886 1886 seq->slotid = be32_to_cpup(p++); 1887 - seq->maxslots = be32_to_cpup(p++); 1887 + /* sa_highest_slotid counts from 0 but maxslots counts from 1 ... */ 1888 + seq->maxslots = be32_to_cpup(p++) + 1; 1888 1889 seq->cachethis = be32_to_cpup(p); 1889 1890 1890 1891 seq->status_flags = 0; ··· 4969 4968 if (nfserr != nfs_ok) 4970 4969 return nfserr; 4971 4970 /* sr_target_highest_slotid */ 4972 - nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1); 4971 + nfserr = nfsd4_encode_slotid4(xdr, seq->target_maxslots - 1); 4973 4972 if (nfserr != nfs_ok) 4974 4973 return nfserr; 4975 4974 /* sr_status_flags */
+5 -1
fs/nfsd/state.h
··· 245 245 struct svc_cred sl_cred; 246 246 u32 sl_datalen; 247 247 u16 sl_opcnt; 248 + u16 sl_generation; 248 249 #define NFSD4_SLOT_INUSE (1 << 0) 249 250 #define NFSD4_SLOT_CACHETHIS (1 << 1) 250 251 #define NFSD4_SLOT_INITIALIZED (1 << 2) 251 252 #define NFSD4_SLOT_CACHED (1 << 3) 253 + #define NFSD4_SLOT_REUSED (1 << 4) 252 254 u8 sl_flags; 253 255 char sl_data[]; 254 256 }; ··· 323 321 u32 se_cb_slot_avail; /* bitmap of available slots */ 324 322 u32 se_cb_highest_slot; /* highest slot client wants */ 325 323 u32 se_cb_prog; 326 - bool se_dead; 327 324 struct list_head se_hash; /* hash by sessionid */ 328 325 struct list_head se_perclnt; 329 326 struct nfs4_client *se_client; ··· 332 331 struct list_head se_conns; 333 332 u32 se_cb_seq_nr[NFSD_BC_SLOT_TABLE_SIZE]; 334 333 struct xarray se_slots; /* forward channel slots */ 334 + u16 se_slot_gen; 335 + bool se_dead; 336 + u32 se_target_maxslots; 335 337 }; 336 338 337 339 /* formatted contents of nfs4_sessionid */
-2
fs/nfsd/xdr4.h
··· 576 576 u32 slotid; /* request/response */ 577 577 u32 maxslots; /* request/response */ 578 578 u32 cachethis; /* request */ 579 - #if 0 580 579 u32 target_maxslots; /* response */ 581 - #endif /* not yet */ 582 580 u32 status_flags; /* response */ 583 581 }; 584 582