162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/nfs/nfs4session.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2012 Trond Myklebust <Trond.Myklebust@netapp.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/errno.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/printk.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/sunrpc/sched.h>
1462306a36Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h>
1562306a36Sopenharmony_ci#include <linux/nfs.h>
1662306a36Sopenharmony_ci#include <linux/nfs4.h>
1762306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "nfs4_fs.h"
2162306a36Sopenharmony_ci#include "internal.h"
2262306a36Sopenharmony_ci#include "nfs4session.h"
2362306a36Sopenharmony_ci#include "callback.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_STATE
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void nfs4_init_slot_table(struct nfs4_slot_table *tbl, const char *queue)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	tbl->highest_used_slotid = NFS4_NO_SLOT;
3062306a36Sopenharmony_ci	spin_lock_init(&tbl->slot_tbl_lock);
3162306a36Sopenharmony_ci	rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, queue);
3262306a36Sopenharmony_ci	init_waitqueue_head(&tbl->slot_waitq);
3362306a36Sopenharmony_ci	init_completion(&tbl->complete);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * nfs4_shrink_slot_table - free retired slots from the slot table
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_cistatic void nfs4_shrink_slot_table(struct nfs4_slot_table  *tbl, u32 newsize)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct nfs4_slot **p;
4262306a36Sopenharmony_ci	if (newsize >= tbl->max_slots)
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	p = &tbl->slots;
4662306a36Sopenharmony_ci	while (newsize--)
4762306a36Sopenharmony_ci		p = &(*p)->next;
4862306a36Sopenharmony_ci	while (*p) {
4962306a36Sopenharmony_ci		struct nfs4_slot *slot = *p;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		*p = slot->next;
5262306a36Sopenharmony_ci		kfree(slot);
5362306a36Sopenharmony_ci		tbl->max_slots--;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * nfs4_slot_tbl_drain_complete - wake waiters when drain is complete
5962306a36Sopenharmony_ci * @tbl: controlling slot table
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_civoid nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (nfs4_slot_tbl_draining(tbl))
6562306a36Sopenharmony_ci		complete(&tbl->complete);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * nfs4_free_slot - free a slot and efficiently update slot table.
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * freeing a slot is trivially done by clearing its respective bit
7262306a36Sopenharmony_ci * in the bitmap.
7362306a36Sopenharmony_ci * If the freed slotid equals highest_used_slotid we want to update it
7462306a36Sopenharmony_ci * so that the server would be able to size down the slot table if needed,
7562306a36Sopenharmony_ci * otherwise we know that the highest_used_slotid is still in use.
7662306a36Sopenharmony_ci * When updating highest_used_slotid there may be "holes" in the bitmap
7762306a36Sopenharmony_ci * so we need to scan down from highest_used_slotid to 0 looking for the now
7862306a36Sopenharmony_ci * highest slotid in use.
7962306a36Sopenharmony_ci * If none found, highest_used_slotid is set to NFS4_NO_SLOT.
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * Must be called while holding tbl->slot_tbl_lock
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_civoid nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	u32 slotid = slot->slot_nr;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* clear used bit in bitmap */
8862306a36Sopenharmony_ci	__clear_bit(slotid, tbl->used_slots);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* update highest_used_slotid when it is freed */
9162306a36Sopenharmony_ci	if (slotid == tbl->highest_used_slotid) {
9262306a36Sopenharmony_ci		u32 new_max = find_last_bit(tbl->used_slots, slotid);
9362306a36Sopenharmony_ci		if (new_max < slotid)
9462306a36Sopenharmony_ci			tbl->highest_used_slotid = new_max;
9562306a36Sopenharmony_ci		else {
9662306a36Sopenharmony_ci			tbl->highest_used_slotid = NFS4_NO_SLOT;
9762306a36Sopenharmony_ci			nfs4_slot_tbl_drain_complete(tbl);
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	dprintk("%s: slotid %u highest_used_slotid %u\n", __func__,
10162306a36Sopenharmony_ci		slotid, tbl->highest_used_slotid);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table  *tbl,
10562306a36Sopenharmony_ci		u32 slotid, u32 seq_init, gfp_t gfp_mask)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct nfs4_slot *slot;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	slot = kzalloc(sizeof(*slot), gfp_mask);
11062306a36Sopenharmony_ci	if (slot) {
11162306a36Sopenharmony_ci		slot->table = tbl;
11262306a36Sopenharmony_ci		slot->slot_nr = slotid;
11362306a36Sopenharmony_ci		slot->seq_nr = seq_init;
11462306a36Sopenharmony_ci		slot->seq_nr_highest_sent = seq_init;
11562306a36Sopenharmony_ci		slot->seq_nr_last_acked = seq_init - 1;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	return slot;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table  *tbl,
12162306a36Sopenharmony_ci		u32 slotid, u32 seq_init, gfp_t gfp_mask)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct nfs4_slot **p, *slot;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	p = &tbl->slots;
12662306a36Sopenharmony_ci	for (;;) {
12762306a36Sopenharmony_ci		if (*p == NULL) {
12862306a36Sopenharmony_ci			*p = nfs4_new_slot(tbl, tbl->max_slots,
12962306a36Sopenharmony_ci					seq_init, gfp_mask);
13062306a36Sopenharmony_ci			if (*p == NULL)
13162306a36Sopenharmony_ci				break;
13262306a36Sopenharmony_ci			tbl->max_slots++;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci		slot = *p;
13562306a36Sopenharmony_ci		if (slot->slot_nr == slotid)
13662306a36Sopenharmony_ci			return slot;
13762306a36Sopenharmony_ci		p = &slot->next;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void nfs4_lock_slot(struct nfs4_slot_table *tbl,
14362306a36Sopenharmony_ci		struct nfs4_slot *slot)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	u32 slotid = slot->slot_nr;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	__set_bit(slotid, tbl->used_slots);
14862306a36Sopenharmony_ci	if (slotid > tbl->highest_used_slotid ||
14962306a36Sopenharmony_ci	    tbl->highest_used_slotid == NFS4_NO_SLOT)
15062306a36Sopenharmony_ci		tbl->highest_used_slotid = slotid;
15162306a36Sopenharmony_ci	slot->generation = tbl->generation;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * nfs4_try_to_lock_slot - Given a slot try to allocate it
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * Note: must be called with the slot_tbl_lock held.
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_cibool nfs4_try_to_lock_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	if (nfs4_test_locked_slot(tbl, slot->slot_nr))
16262306a36Sopenharmony_ci		return false;
16362306a36Sopenharmony_ci	nfs4_lock_slot(tbl, slot);
16462306a36Sopenharmony_ci	return true;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/*
16862306a36Sopenharmony_ci * nfs4_lookup_slot - Find a slot but don't allocate it
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * Note: must be called with the slot_tbl_lock held.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistruct nfs4_slot *nfs4_lookup_slot(struct nfs4_slot_table *tbl, u32 slotid)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	if (slotid <= tbl->max_slotid)
17562306a36Sopenharmony_ci		return nfs4_find_or_create_slot(tbl, slotid, 0, GFP_NOWAIT);
17662306a36Sopenharmony_ci	return ERR_PTR(-E2BIG);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int nfs4_slot_get_seqid(struct nfs4_slot_table  *tbl, u32 slotid,
18062306a36Sopenharmony_ci		u32 *seq_nr)
18162306a36Sopenharmony_ci	__must_hold(&tbl->slot_tbl_lock)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct nfs4_slot *slot;
18462306a36Sopenharmony_ci	int ret;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	slot = nfs4_lookup_slot(tbl, slotid);
18762306a36Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(slot);
18862306a36Sopenharmony_ci	if (!ret)
18962306a36Sopenharmony_ci		*seq_nr = slot->seq_nr;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return ret;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * nfs4_slot_seqid_in_use - test if a slot sequence id is still in use
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Given a slot table, slot id and sequence number, determine if the
19862306a36Sopenharmony_ci * RPC call in question is still in flight. This function is mainly
19962306a36Sopenharmony_ci * intended for use by the callback channel.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_cistatic bool nfs4_slot_seqid_in_use(struct nfs4_slot_table *tbl,
20262306a36Sopenharmony_ci		u32 slotid, u32 seq_nr)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	u32 cur_seq = 0;
20562306a36Sopenharmony_ci	bool ret = false;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	spin_lock(&tbl->slot_tbl_lock);
20862306a36Sopenharmony_ci	if (nfs4_slot_get_seqid(tbl, slotid, &cur_seq) == 0 &&
20962306a36Sopenharmony_ci	    cur_seq == seq_nr && test_bit(slotid, tbl->used_slots))
21062306a36Sopenharmony_ci		ret = true;
21162306a36Sopenharmony_ci	spin_unlock(&tbl->slot_tbl_lock);
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * nfs4_slot_wait_on_seqid - wait until a slot sequence id is complete
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * Given a slot table, slot id and sequence number, wait until the
21962306a36Sopenharmony_ci * corresponding RPC call completes. This function is mainly
22062306a36Sopenharmony_ci * intended for use by the callback channel.
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_ciint nfs4_slot_wait_on_seqid(struct nfs4_slot_table *tbl,
22362306a36Sopenharmony_ci		u32 slotid, u32 seq_nr,
22462306a36Sopenharmony_ci		unsigned long timeout)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	if (wait_event_timeout(tbl->slot_waitq,
22762306a36Sopenharmony_ci			!nfs4_slot_seqid_in_use(tbl, slotid, seq_nr),
22862306a36Sopenharmony_ci			timeout) == 0)
22962306a36Sopenharmony_ci		return -ETIMEDOUT;
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * nfs4_alloc_slot - efficiently look for a free slot
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci * nfs4_alloc_slot looks for an unset bit in the used_slots bitmap.
23762306a36Sopenharmony_ci * If found, we mark the slot as used, update the highest_used_slotid,
23862306a36Sopenharmony_ci * and respectively set up the sequence operation args.
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci * Note: must be called with under the slot_tbl_lock.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistruct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct nfs4_slot *ret = ERR_PTR(-EBUSY);
24562306a36Sopenharmony_ci	u32 slotid;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n",
24862306a36Sopenharmony_ci		__func__, tbl->used_slots[0], tbl->highest_used_slotid,
24962306a36Sopenharmony_ci		tbl->max_slotid + 1);
25062306a36Sopenharmony_ci	slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1);
25162306a36Sopenharmony_ci	if (slotid <= tbl->max_slotid) {
25262306a36Sopenharmony_ci		ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT);
25362306a36Sopenharmony_ci		if (!IS_ERR(ret))
25462306a36Sopenharmony_ci			nfs4_lock_slot(tbl, ret);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	dprintk("<-- %s used_slots=%04lx highest_used=%u slotid=%u\n",
25762306a36Sopenharmony_ci		__func__, tbl->used_slots[0], tbl->highest_used_slotid,
25862306a36Sopenharmony_ci		!IS_ERR(ret) ? ret->slot_nr : NFS4_NO_SLOT);
25962306a36Sopenharmony_ci	return ret;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int nfs4_grow_slot_table(struct nfs4_slot_table *tbl,
26362306a36Sopenharmony_ci		 u32 max_reqs, u32 ivalue)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	if (max_reqs <= tbl->max_slots)
26662306a36Sopenharmony_ci		return 0;
26762306a36Sopenharmony_ci	if (!IS_ERR(nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS)))
26862306a36Sopenharmony_ci		return 0;
26962306a36Sopenharmony_ci	return -ENOMEM;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void nfs4_reset_slot_table(struct nfs4_slot_table *tbl,
27362306a36Sopenharmony_ci		u32 server_highest_slotid,
27462306a36Sopenharmony_ci		u32 ivalue)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct nfs4_slot **p;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	nfs4_shrink_slot_table(tbl, server_highest_slotid + 1);
27962306a36Sopenharmony_ci	p = &tbl->slots;
28062306a36Sopenharmony_ci	while (*p) {
28162306a36Sopenharmony_ci		(*p)->seq_nr = ivalue;
28262306a36Sopenharmony_ci		(*p)->seq_nr_highest_sent = ivalue;
28362306a36Sopenharmony_ci		(*p)->seq_nr_last_acked = ivalue - 1;
28462306a36Sopenharmony_ci		p = &(*p)->next;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	tbl->highest_used_slotid = NFS4_NO_SLOT;
28762306a36Sopenharmony_ci	tbl->target_highest_slotid = server_highest_slotid;
28862306a36Sopenharmony_ci	tbl->server_highest_slotid = server_highest_slotid;
28962306a36Sopenharmony_ci	tbl->d_target_highest_slotid = 0;
29062306a36Sopenharmony_ci	tbl->d2_target_highest_slotid = 0;
29162306a36Sopenharmony_ci	tbl->max_slotid = server_highest_slotid;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci * (re)Initialise a slot table
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_cistatic int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl,
29862306a36Sopenharmony_ci		u32 max_reqs, u32 ivalue)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	int ret;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	dprintk("--> %s: max_reqs=%u, tbl->max_slots %u\n", __func__,
30362306a36Sopenharmony_ci		max_reqs, tbl->max_slots);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (max_reqs > NFS4_MAX_SLOT_TABLE)
30662306a36Sopenharmony_ci		max_reqs = NFS4_MAX_SLOT_TABLE;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue);
30962306a36Sopenharmony_ci	if (ret)
31062306a36Sopenharmony_ci		goto out;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	spin_lock(&tbl->slot_tbl_lock);
31362306a36Sopenharmony_ci	nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue);
31462306a36Sopenharmony_ci	spin_unlock(&tbl->slot_tbl_lock);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	dprintk("%s: tbl=%p slots=%p max_slots=%u\n", __func__,
31762306a36Sopenharmony_ci		tbl, tbl->slots, tbl->max_slots);
31862306a36Sopenharmony_ciout:
31962306a36Sopenharmony_ci	dprintk("<-- %s: return %d\n", __func__, ret);
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/*
32462306a36Sopenharmony_ci * nfs4_release_slot_table - release all slot table entries
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_cistatic void nfs4_release_slot_table(struct nfs4_slot_table *tbl)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	nfs4_shrink_slot_table(tbl, 0);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/**
33262306a36Sopenharmony_ci * nfs4_shutdown_slot_table - release resources attached to a slot table
33362306a36Sopenharmony_ci * @tbl: slot table to shut down
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_civoid nfs4_shutdown_slot_table(struct nfs4_slot_table *tbl)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	nfs4_release_slot_table(tbl);
33962306a36Sopenharmony_ci	rpc_destroy_wait_queue(&tbl->slot_tbl_waitq);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * nfs4_setup_slot_table - prepare a stand-alone slot table for use
34462306a36Sopenharmony_ci * @tbl: slot table to set up
34562306a36Sopenharmony_ci * @max_reqs: maximum number of requests allowed
34662306a36Sopenharmony_ci * @queue: name to give RPC wait queue
34762306a36Sopenharmony_ci *
34862306a36Sopenharmony_ci * Returns zero on success, or a negative errno.
34962306a36Sopenharmony_ci */
35062306a36Sopenharmony_ciint nfs4_setup_slot_table(struct nfs4_slot_table *tbl, unsigned int max_reqs,
35162306a36Sopenharmony_ci		const char *queue)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	nfs4_init_slot_table(tbl, queue);
35462306a36Sopenharmony_ci	return nfs4_realloc_slot_table(tbl, max_reqs, 0);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic bool nfs41_assign_slot(struct rpc_task *task, void *pslot)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct nfs4_sequence_args *args = task->tk_msg.rpc_argp;
36062306a36Sopenharmony_ci	struct nfs4_sequence_res *res = task->tk_msg.rpc_resp;
36162306a36Sopenharmony_ci	struct nfs4_slot *slot = pslot;
36262306a36Sopenharmony_ci	struct nfs4_slot_table *tbl = slot->table;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged)
36562306a36Sopenharmony_ci		return false;
36662306a36Sopenharmony_ci	slot->generation = tbl->generation;
36762306a36Sopenharmony_ci	args->sa_slot = slot;
36862306a36Sopenharmony_ci	res->sr_timestamp = jiffies;
36962306a36Sopenharmony_ci	res->sr_slot = slot;
37062306a36Sopenharmony_ci	res->sr_status_flags = 0;
37162306a36Sopenharmony_ci	res->sr_status = 1;
37262306a36Sopenharmony_ci	return true;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic bool __nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl,
37662306a36Sopenharmony_ci		struct nfs4_slot *slot)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	if (rpc_wake_up_first(&tbl->slot_tbl_waitq, nfs41_assign_slot, slot))
37962306a36Sopenharmony_ci		return true;
38062306a36Sopenharmony_ci	return false;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cibool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl,
38462306a36Sopenharmony_ci		struct nfs4_slot *slot)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	if (slot->slot_nr > tbl->max_slotid)
38762306a36Sopenharmony_ci		return false;
38862306a36Sopenharmony_ci	return __nfs41_wake_and_assign_slot(tbl, slot);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic bool nfs41_try_wake_next_slot_table_entry(struct nfs4_slot_table *tbl)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct nfs4_slot *slot = nfs4_alloc_slot(tbl);
39462306a36Sopenharmony_ci	if (!IS_ERR(slot)) {
39562306a36Sopenharmony_ci		bool ret = __nfs41_wake_and_assign_slot(tbl, slot);
39662306a36Sopenharmony_ci		if (ret)
39762306a36Sopenharmony_ci			return ret;
39862306a36Sopenharmony_ci		nfs4_free_slot(tbl, slot);
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci	return false;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_civoid nfs41_wake_slot_table(struct nfs4_slot_table *tbl)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	for (;;) {
40662306a36Sopenharmony_ci		if (!nfs41_try_wake_next_slot_table_entry(tbl))
40762306a36Sopenharmony_ci			break;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1)
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void nfs41_set_max_slotid_locked(struct nfs4_slot_table *tbl,
41462306a36Sopenharmony_ci		u32 target_highest_slotid)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	u32 max_slotid;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, target_highest_slotid);
41962306a36Sopenharmony_ci	if (max_slotid > tbl->server_highest_slotid)
42062306a36Sopenharmony_ci		max_slotid = tbl->server_highest_slotid;
42162306a36Sopenharmony_ci	if (max_slotid > tbl->target_highest_slotid)
42262306a36Sopenharmony_ci		max_slotid = tbl->target_highest_slotid;
42362306a36Sopenharmony_ci	tbl->max_slotid = max_slotid;
42462306a36Sopenharmony_ci	nfs41_wake_slot_table(tbl);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/* Update the client's idea of target_highest_slotid */
42862306a36Sopenharmony_cistatic void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl,
42962306a36Sopenharmony_ci		u32 target_highest_slotid)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	if (tbl->target_highest_slotid == target_highest_slotid)
43262306a36Sopenharmony_ci		return;
43362306a36Sopenharmony_ci	tbl->target_highest_slotid = target_highest_slotid;
43462306a36Sopenharmony_ci	tbl->generation++;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_civoid nfs41_set_target_slotid(struct nfs4_slot_table *tbl,
43862306a36Sopenharmony_ci		u32 target_highest_slotid)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	spin_lock(&tbl->slot_tbl_lock);
44162306a36Sopenharmony_ci	nfs41_set_target_slotid_locked(tbl, target_highest_slotid);
44262306a36Sopenharmony_ci	tbl->d_target_highest_slotid = 0;
44362306a36Sopenharmony_ci	tbl->d2_target_highest_slotid = 0;
44462306a36Sopenharmony_ci	nfs41_set_max_slotid_locked(tbl, target_highest_slotid);
44562306a36Sopenharmony_ci	spin_unlock(&tbl->slot_tbl_lock);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl,
44962306a36Sopenharmony_ci		u32 highest_slotid)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	if (tbl->server_highest_slotid == highest_slotid)
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci	if (tbl->highest_used_slotid > highest_slotid)
45462306a36Sopenharmony_ci		return;
45562306a36Sopenharmony_ci	/* Deallocate slots */
45662306a36Sopenharmony_ci	nfs4_shrink_slot_table(tbl, highest_slotid + 1);
45762306a36Sopenharmony_ci	tbl->server_highest_slotid = highest_slotid;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic s32 nfs41_derivative_target_slotid(s32 s1, s32 s2)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	s1 -= s2;
46362306a36Sopenharmony_ci	if (s1 == 0)
46462306a36Sopenharmony_ci		return 0;
46562306a36Sopenharmony_ci	if (s1 < 0)
46662306a36Sopenharmony_ci		return (s1 - 1) >> 1;
46762306a36Sopenharmony_ci	return (s1 + 1) >> 1;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int nfs41_sign_s32(s32 s1)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	if (s1 > 0)
47362306a36Sopenharmony_ci		return 1;
47462306a36Sopenharmony_ci	if (s1 < 0)
47562306a36Sopenharmony_ci		return -1;
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic bool nfs41_same_sign_or_zero_s32(s32 s1, s32 s2)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	if (!s1 || !s2)
48262306a36Sopenharmony_ci		return true;
48362306a36Sopenharmony_ci	return nfs41_sign_s32(s1) == nfs41_sign_s32(s2);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* Try to eliminate outliers by checking for sharp changes in the
48762306a36Sopenharmony_ci * derivatives and second derivatives
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_cistatic bool nfs41_is_outlier_target_slotid(struct nfs4_slot_table *tbl,
49062306a36Sopenharmony_ci		u32 new_target)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	s32 d_target, d2_target;
49362306a36Sopenharmony_ci	bool ret = true;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	d_target = nfs41_derivative_target_slotid(new_target,
49662306a36Sopenharmony_ci			tbl->target_highest_slotid);
49762306a36Sopenharmony_ci	d2_target = nfs41_derivative_target_slotid(d_target,
49862306a36Sopenharmony_ci			tbl->d_target_highest_slotid);
49962306a36Sopenharmony_ci	/* Is first derivative same sign? */
50062306a36Sopenharmony_ci	if (nfs41_same_sign_or_zero_s32(d_target, tbl->d_target_highest_slotid))
50162306a36Sopenharmony_ci		ret = false;
50262306a36Sopenharmony_ci	/* Is second derivative same sign? */
50362306a36Sopenharmony_ci	if (nfs41_same_sign_or_zero_s32(d2_target, tbl->d2_target_highest_slotid))
50462306a36Sopenharmony_ci		ret = false;
50562306a36Sopenharmony_ci	tbl->d_target_highest_slotid = d_target;
50662306a36Sopenharmony_ci	tbl->d2_target_highest_slotid = d2_target;
50762306a36Sopenharmony_ci	return ret;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_civoid nfs41_update_target_slotid(struct nfs4_slot_table *tbl,
51162306a36Sopenharmony_ci		struct nfs4_slot *slot,
51262306a36Sopenharmony_ci		struct nfs4_sequence_res *res)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	u32 target_highest_slotid = min(res->sr_target_highest_slotid,
51562306a36Sopenharmony_ci					NFS4_MAX_SLOTID);
51662306a36Sopenharmony_ci	u32 highest_slotid = min(res->sr_highest_slotid, NFS4_MAX_SLOTID);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	spin_lock(&tbl->slot_tbl_lock);
51962306a36Sopenharmony_ci	if (!nfs41_is_outlier_target_slotid(tbl, target_highest_slotid))
52062306a36Sopenharmony_ci		nfs41_set_target_slotid_locked(tbl, target_highest_slotid);
52162306a36Sopenharmony_ci	if (tbl->generation == slot->generation)
52262306a36Sopenharmony_ci		nfs41_set_server_slotid_locked(tbl, highest_slotid);
52362306a36Sopenharmony_ci	nfs41_set_max_slotid_locked(tbl, target_highest_slotid);
52462306a36Sopenharmony_ci	spin_unlock(&tbl->slot_tbl_lock);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void nfs4_release_session_slot_tables(struct nfs4_session *session)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	nfs4_release_slot_table(&session->fc_slot_table);
53062306a36Sopenharmony_ci	nfs4_release_slot_table(&session->bc_slot_table);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/*
53462306a36Sopenharmony_ci * Initialize or reset the forechannel and backchannel tables
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_ciint nfs4_setup_session_slot_tables(struct nfs4_session *ses)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct nfs4_slot_table *tbl;
53962306a36Sopenharmony_ci	int status;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	dprintk("--> %s\n", __func__);
54262306a36Sopenharmony_ci	/* Fore channel */
54362306a36Sopenharmony_ci	tbl = &ses->fc_slot_table;
54462306a36Sopenharmony_ci	tbl->session = ses;
54562306a36Sopenharmony_ci	status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1);
54662306a36Sopenharmony_ci	if (status || !(ses->flags & SESSION4_BACK_CHAN)) /* -ENOMEM */
54762306a36Sopenharmony_ci		return status;
54862306a36Sopenharmony_ci	/* Back channel */
54962306a36Sopenharmony_ci	tbl = &ses->bc_slot_table;
55062306a36Sopenharmony_ci	tbl->session = ses;
55162306a36Sopenharmony_ci	status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0);
55262306a36Sopenharmony_ci	if (status && tbl->slots == NULL)
55362306a36Sopenharmony_ci		/* Fore and back channel share a connection so get
55462306a36Sopenharmony_ci		 * both slot tables or neither */
55562306a36Sopenharmony_ci		nfs4_release_session_slot_tables(ses);
55662306a36Sopenharmony_ci	return status;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistruct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct nfs4_session *session;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
56462306a36Sopenharmony_ci	if (!session)
56562306a36Sopenharmony_ci		return NULL;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	nfs4_init_slot_table(&session->fc_slot_table, "ForeChannel Slot table");
56862306a36Sopenharmony_ci	nfs4_init_slot_table(&session->bc_slot_table, "BackChannel Slot table");
56962306a36Sopenharmony_ci	session->session_state = 1<<NFS4_SESSION_INITING;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	session->clp = clp;
57262306a36Sopenharmony_ci	return session;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic void nfs4_destroy_session_slot_tables(struct nfs4_session *session)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	nfs4_shutdown_slot_table(&session->fc_slot_table);
57862306a36Sopenharmony_ci	nfs4_shutdown_slot_table(&session->bc_slot_table);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_civoid nfs4_destroy_session(struct nfs4_session *session)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct rpc_xprt *xprt;
58462306a36Sopenharmony_ci	const struct cred *cred;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	cred = nfs4_get_clid_cred(session->clp);
58762306a36Sopenharmony_ci	nfs4_proc_destroy_session(session, cred);
58862306a36Sopenharmony_ci	put_cred(cred);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	rcu_read_lock();
59162306a36Sopenharmony_ci	xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt);
59262306a36Sopenharmony_ci	rcu_read_unlock();
59362306a36Sopenharmony_ci	dprintk("%s Destroy backchannel for xprt %p\n",
59462306a36Sopenharmony_ci		__func__, xprt);
59562306a36Sopenharmony_ci	xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS);
59662306a36Sopenharmony_ci	nfs4_destroy_session_slot_tables(session);
59762306a36Sopenharmony_ci	kfree(session);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/*
60162306a36Sopenharmony_ci * With sessions, the client is not marked ready until after a
60262306a36Sopenharmony_ci * successful EXCHANGE_ID and CREATE_SESSION.
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
60562306a36Sopenharmony_ci * other versions of NFS can be tried.
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_cistatic int nfs41_check_session_ready(struct nfs_client *clp)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	int ret;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (clp->cl_cons_state == NFS_CS_SESSION_INITING) {
61262306a36Sopenharmony_ci		ret = nfs4_client_recover_expired_lease(clp);
61362306a36Sopenharmony_ci		if (ret)
61462306a36Sopenharmony_ci			return ret;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci	if (clp->cl_cons_state < NFS_CS_READY)
61762306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
61862306a36Sopenharmony_ci	smp_rmb();
61962306a36Sopenharmony_ci	return 0;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ciint nfs4_init_session(struct nfs_client *clp)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	if (!nfs4_has_session(clp))
62562306a36Sopenharmony_ci		return 0;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	clear_bit(NFS4_SESSION_INITING, &clp->cl_session->session_state);
62862306a36Sopenharmony_ci	return nfs41_check_session_ready(clp);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ciint nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct nfs4_session *session = clp->cl_session;
63462306a36Sopenharmony_ci	int ret;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
63762306a36Sopenharmony_ci	if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
63862306a36Sopenharmony_ci		/*
63962306a36Sopenharmony_ci		 * Do not set NFS_CS_CHECK_LEASE_TIME instead set the
64062306a36Sopenharmony_ci		 * DS lease to be equal to the MDS lease.
64162306a36Sopenharmony_ci		 */
64262306a36Sopenharmony_ci		clp->cl_lease_time = lease_time;
64362306a36Sopenharmony_ci		clp->cl_last_renewal = jiffies;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	ret = nfs41_check_session_ready(clp);
64862306a36Sopenharmony_ci	if (ret)
64962306a36Sopenharmony_ci		return ret;
65062306a36Sopenharmony_ci	/* Test for the DS role */
65162306a36Sopenharmony_ci	if (!is_ds_client(clp))
65262306a36Sopenharmony_ci		return -ENODEV;
65362306a36Sopenharmony_ci	return 0;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_init_ds_session);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci#endif	/* defined(CONFIG_NFS_V4_1) */
658