Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f86f36a6 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4.1: Fix a NFSv4.1 session initialisation regression



Commit aacd5537 (NFSv4.1: cleanup init and reset of session slot tables)
introduces a regression in the session initialisation code. New tables
now find their sequence ids initialised to 0, rather than the mandated
value of 1 (see RFC5661).

Fix the problem by merging nfs4_reset_slot_table() and nfs4_init_slot_table().
Since the tbl->max_slots is initialised to 0, the test in
nfs4_reset_slot_table for max_reqs != tbl->max_slots will automatically
pass for an empty table.

Reported-by: default avatarVitaliy Gusev <gusev.vitaliy@nexenta.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent b9f9a031
Loading
Loading
Loading
Loading
+42 −65
Original line number Diff line number Diff line
@@ -5008,37 +5008,53 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
	return status;
}

static struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags)
{
	return kcalloc(max_slots, sizeof(struct nfs4_slot), gfp_flags);
}

static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl,
		struct nfs4_slot *new,
		u32 max_slots,
		u32 ivalue)
{
	struct nfs4_slot *old = NULL;
	u32 i;

	spin_lock(&tbl->slot_tbl_lock);
	if (new) {
		old = tbl->slots;
		tbl->slots = new;
		tbl->max_slots = max_slots;
	}
	tbl->highest_used_slotid = -1;	/* no slot is currently used */
	for (i = 0; i < tbl->max_slots; i++)
		tbl->slots[i].seq_nr = ivalue;
	spin_unlock(&tbl->slot_tbl_lock);
	kfree(old);
}

/*
 * Reset a slot table
 * (re)Initialise a slot table
 */
static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs,
				 int ivalue)
static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs,
				 u32 ivalue)
{
	struct nfs4_slot *new = NULL;
	int i;
	int ret = 0;
	int ret = -ENOMEM;

	dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__,
		max_reqs, tbl->max_slots);

	/* Does the newly negotiated max_reqs match the existing slot table? */
	if (max_reqs != tbl->max_slots) {
		ret = -ENOMEM;
		new = kmalloc(max_reqs * sizeof(struct nfs4_slot),
			      GFP_NOFS);
		new = nfs4_alloc_slots(max_reqs, GFP_NOFS);
		if (!new)
			goto out;
		ret = 0;
		kfree(tbl->slots);
	}
	spin_lock(&tbl->slot_tbl_lock);
	if (new) {
		tbl->slots = new;
		tbl->max_slots = max_reqs;
	}
	for (i = 0; i < tbl->max_slots; ++i)
		tbl->slots[i].seq_nr = ivalue;
	spin_unlock(&tbl->slot_tbl_lock);
	ret = 0;

	nfs4_add_and_init_slots(tbl, new, max_reqs, ivalue);
	dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
		tbl, tbl->slots, tbl->max_slots);
out:
@@ -5060,36 +5076,6 @@ static void nfs4_destroy_slot_tables(struct nfs4_session *session)
	return;
}

/*
 * Initialize slot table
 */
static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
		int max_slots, int ivalue)
{
	struct nfs4_slot *slot;
	int ret = -ENOMEM;

	BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE);

	dprintk("--> %s: max_reqs=%u\n", __func__, max_slots);

	slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_NOFS);
	if (!slot)
		goto out;
	ret = 0;

	spin_lock(&tbl->slot_tbl_lock);
	tbl->max_slots = max_slots;
	tbl->slots = slot;
	tbl->highest_used_slotid = -1;  /* no slot is currently used */
	spin_unlock(&tbl->slot_tbl_lock);
	dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
		tbl, tbl->slots, tbl->max_slots);
out:
	dprintk("<-- %s: return %d\n", __func__, ret);
	return ret;
}

/*
 * Initialize or reset the forechannel and backchannel tables
 */
@@ -5101,25 +5087,16 @@ static int nfs4_setup_session_slot_tables(struct nfs4_session *ses)
	dprintk("--> %s\n", __func__);
	/* Fore channel */
	tbl = &ses->fc_slot_table;
	if (tbl->slots == NULL) {
		status = nfs4_init_slot_table(tbl, ses->fc_attrs.max_reqs, 1);
	status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1);
	if (status) /* -ENOMEM */
		return status;
	} else {
		status = nfs4_reset_slot_table(tbl, ses->fc_attrs.max_reqs, 1);
		if (status)
			return status;
	}
	/* Back channel */
	tbl = &ses->bc_slot_table;
	if (tbl->slots == NULL) {
		status = nfs4_init_slot_table(tbl, ses->bc_attrs.max_reqs, 0);
		if (status)
	status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0);
	if (status && tbl->slots == NULL)
		/* Fore and back channel share a connection so get
		 * both slot tables or neither */
		nfs4_destroy_slot_tables(ses);
	} else
		status = nfs4_reset_slot_table(tbl, ses->bc_attrs.max_reqs, 0);
	return status;
}