[pnfs] [PATCH 05/10] [nfs41-2.6.25] nfs41: use bitmap-based allocation for sessions slots

Benny Halevy bhalevy at panasas.com
Thu Mar 20 12:22:09 EDT 2008


Also,
* Maintain highest_slotid correctly.
* sleep for a free slot only if failed to allocate one.

Signed-off-by: Benny Halevy <bhalevy at panasas.com>
---
 fs/nfs/nfs4proc.c            |  198 ++++++++++++++++++++++++++++++------------
 include/linux/nfs4_session.h |   16 ++--
 2 files changed, 151 insertions(+), 63 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 1c93a25..4870f67 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -210,6 +210,65 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
 }
 
 #if defined(CONFIG_NFS_V4_1)
+
+static inline unsigned long *idx2bmp(unsigned long *base, int idx, int *bit)
+{
+	base += idx / BITS_PER_LONG;
+	if (bit)
+		*bit = idx & (BITS_PER_LONG - 1);
+	return base;
+}
+
+static inline int bmp2idx(unsigned long *base, unsigned long *bmp,
+			  int bit)
+{
+	return ((bmp - base) * BITS_PER_LONG) + bit;
+}
+
+static inline void nfs41_free_slot(struct nfs4_slot_table *tbl,
+				   struct nfs4_slot *slot)
+{
+	int slotid = slot_idx(tbl, slot);
+	unsigned long *used, u;
+	int bit;
+
+	spin_lock(&tbl->slot_tbl_lock);
+	/* clear used bit in bitmap */
+	used = idx2bmp(tbl->used_slots, slotid, &bit);
+	*used &= ~(1 << bit);
+
+	if (slotid < tbl->lowest_free_slotid)
+		tbl->lowest_free_slotid = slotid;
+
+	/* update highest_used_slotid when it is freed */
+	if (slotid == tbl->highest_used_slotid) {
+		tbl->highest_used_slotid = -1;
+		slotid--;
+		/* scan down for the highest used slotid */
+		while (slotid >= 0) {
+			used = idx2bmp(tbl->used_slots, slotid, NULL);
+			u = *used;
+			switch (u) {
+			case 0:
+				/* all slots marked free in this word */
+				slotid -= BITS_PER_LONG;
+				continue;
+			case ~0UL:
+				/* special case __fls can't handle */
+				bit = BITS_PER_LONG - 1;
+				break;
+			default:
+				bit = __fls(u);
+			}
+			tbl->highest_used_slotid = bmp2idx(tbl->used_slots,
+							   used, bit);
+			break;
+		}
+	}
+	spin_unlock(&tbl->slot_tbl_lock);
+	rpc_wake_up_next(&tbl->slot_tbl_waitq);
+}
+
 /* For pNFS filelayout data servers:
  * the nfs_client is NULL - to signal no lease update.
  * session is the data server.
@@ -235,7 +294,7 @@ static int nfs41_sequence_done(struct nfs_client *clp,
 	slot = res->sr_slot;
 	if (slot == NULL) {
 		dprintk("%s: no slot: status %d\n", __func__, status);
-		goto out;	/* session recovery probably failed */
+		goto ret;	/* session recovery probably failed */
 	}
 
 	switch (status) {
@@ -267,12 +326,7 @@ static int nfs41_sequence_done(struct nfs_client *clp,
 		spin_unlock(&clp->cl_lock);
 	}
 no_update:
-	/* Clear the 'busy' bit on the slot that was used */
-	smp_mb__before_clear_bit();
-	clear_bit(NFS4_SLOT_BUSY, &slot->flags);
-	smp_mb__after_clear_bit();
-out:
-	rpc_wake_up_next(&tbl->slot_tbl_waitq);
+	nfs41_free_slot(tbl, slot);
 ret:
 	return status;
 }
@@ -299,33 +353,46 @@ static int nfs4_sequence_done(struct nfs_server *server,
 	return ret;
 }
 
-static struct nfs4_slot *__nfs4_find_slot(struct nfs4_slot_table *tbl)
+static inline struct nfs4_slot *nfs4_find_slot(struct nfs4_slot_table *tbl,
+					       struct rpc_task *task,
+					       struct nfs41_sequence_args *args)
 {
-	int i;
-	struct nfs4_slot *slot;
+	int bit, slotid;
+	struct nfs4_slot *slot = NULL;
+	unsigned long *used, u;
 
-	for (i = 0; i < tbl->max_slots; ++i) {
-		slot = &tbl->slots[i];
-		if (!test_bit(NFS4_SLOT_BUSY, &slot->flags)) {
-			set_bit(NFS4_SLOT_BUSY, &slot->flags);
-			return slot;
+	spin_lock(&tbl->slot_tbl_lock);
+	slotid = tbl->lowest_free_slotid;
+	while (slotid < tbl->max_slots) {
+		used = idx2bmp(tbl->used_slots, slotid, NULL);
+		u = *used;
+		if (u == ~0UL) {
+			/* all slots marked used in this word */
+			slotid += BITS_PER_LONG;
+			continue;
 		}
+		/* find first slot marked free in word */
+		bit = ffz(u);
+		slotid = bmp2idx(tbl->used_slots, used, bit);
+		/* mark slot used.  note that we don't do that if
+		 * slotid >= tbl->max_slots so not to confuse
+		 * highest_used_slotid algorithm */
+		if (slotid < tbl->max_slots) {
+			*used |= (1 << bit);
+			if (slotid > tbl->highest_used_slotid)
+				tbl->highest_used_slotid = slotid;
+			slot = tbl->slots + slotid;
+			args->sa_seqid = slot->seq_nr;
+			args->sa_slotid = slotid;
+			args->sa_max_slotid = tbl->highest_used_slotid;
+			slotid++;	/* for updating lowest_free_slot */
+		}
+		break;
 	}
-
-	return NULL;
-}
-
-static struct nfs4_slot *nfs4_find_slot(struct nfs4_slot_table *tbl,
-					struct rpc_task *task)
-{
-	struct nfs4_slot *slot;
-
-	rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL, NULL);
-
-	spin_lock(&tbl->slot_tbl_lock);
-	slot = __nfs4_find_slot(tbl);
+	tbl->lowest_free_slotid = slotid;
 	spin_unlock(&tbl->slot_tbl_lock);
-
+	dprintk("<-- %s slot %p slotid %d\n", __func__,
+		slot, slot ? slot_idx(tbl, slot) : -1);
 	return slot;
 }
 
@@ -338,21 +405,22 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
 	struct nfs4_slot *slot;
 	struct nfs4_slot_table *tbl;
 
+	dprintk("--> %s\n", __func__);
 	tbl = &session->fore_channel.slot_table;
-	slot = nfs4_find_slot(tbl, task);
+	slot = nfs4_find_slot(tbl, task, args);
 
-	if (!slot)
+	if (!slot) {
+		rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL, NULL);
+		dprintk("<-- %s: no free slots\n", __func__);
 		return -EAGAIN;
+	}
 
 	memcpy(args->sa_sessionid.data, session->sess_id,
 	       NFS4_MAX_SESSIONID_LEN);
-	args->sa_slotid = slot->slot_nr;
-	args->sa_seqid = slot->seq_nr;
 	args->sa_cache_this = cache_reply;
 
-	spin_lock(&tbl->slot_tbl_lock);
-	args->sa_max_slotid = tbl->max_slots;
-	spin_unlock(&tbl->slot_tbl_lock);
+	dprintk("<-- %s slot=%p slotid=%d seqid=%d\n", __func__,
+		slot, args->sa_slotid, slot->seq_nr);
 
 	res->sr_slot = slot;
 	res->sr_renewal_time = jiffies;
@@ -4525,37 +4593,53 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp,
 /* Initialize a slot table */
 int nfs4_init_slot_table(struct nfs4_channel *channel)
 {
-	int i;
-	struct nfs4_slot_table *tbl;
+	struct nfs4_slot_table *tbl = &channel->slot_table;
+	int i, max_slots = channel->chan_attrs.max_reqs;
 	struct nfs4_slot *slot;
-	int ret = 0;
+	unsigned long *used;
+	int ret = -ENOMEM;
 
-	slot = kzalloc(channel->chan_attrs.max_reqs * sizeof(struct nfs4_slot),
-		       GFP_ATOMIC);
+	dprintk("--> %s: max_reqs=%u\n", __func__,
+		channel->chan_attrs.max_reqs);
+	slot = kzalloc(max_slots * sizeof(struct nfs4_slot), GFP_ATOMIC);
 	if (!slot)
-		return -ENOMEM;
+		goto out;
+	for (i = 0; i < max_slots; ++i)
+		slot[i].seq_nr = 1;
 
-	tbl = &channel->slot_table;
+	if (max_slots <= BITS_PER_LONG)
+		used = &tbl->_used_slots;	/* use word in tbl */
+	else {
+		used = kzalloc(max_slots * sizeof(*used), GFP_ATOMIC);
+		if (!used)
+			goto out_free;
+	}
+	ret = 0;
 
 	spin_lock(&tbl->slot_tbl_lock);
 	if (tbl->slots != NULL) {
-		kfree(slot);
-		goto out;
+		spin_unlock(&tbl->slot_tbl_lock);
+		dprintk("%s: slot table already initialized. tbl=%p slots=%p\n",
+			__func__, tbl, tbl->slots);
+		WARN_ON(1);
+		goto out_free;
 	}
-
-	tbl->max_slots = channel->chan_attrs.max_reqs;
+	tbl->max_slots = max_slots;
 	tbl->slots = slot;
-
-	for (i = 0; i < channel->chan_attrs.max_reqs; ++i) {
-		slot = &tbl->slots[i];
-		slot->slot_nr = i;
-		slot->seq_nr = 1;
-		slot->flags = 0;
-	}
-
-out:
+	tbl->used_slots = used;
+	tbl->lowest_free_slotid = 0;
+	tbl->highest_used_slotid = -1;
 	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;
+out_free:
+	kfree(slot);
+	if (used != &tbl->_used_slots)
+		kfree(used);
+	goto out;
 }
 
 /* Destroy the slot table */
@@ -4566,6 +4650,8 @@ static void nfs4_destroy_slot_table(struct nfs4_channel *channel)
 	 */
 	kfree(channel->slot_table.slots);
 	channel->slot_table.slots = NULL;
+	if (channel->slot_table.used_slots != &channel->slot_table._used_slots)
+		kfree(channel->slot_table.used_slots);
 
 	return;
 }
diff --git a/include/linux/nfs4_session.h b/include/linux/nfs4_session.h
index 8ef8d6d..64434ec 100644
--- a/include/linux/nfs4_session.h
+++ b/include/linux/nfs4_session.h
@@ -7,11 +7,6 @@
 #include <linux/smp_lock.h>
 #include <linux/sunrpc/sched.h>
 
-/* The flags for the nfs4_slot struct */
-#define NFS4_SLOT_BUSY		0X0	/* Slot in use */
-#define NFS4_SLOT_RECLAIMED	0x1	/* Slot has been reclaimed by
-					   the server */
-
 struct nfs4_channel_attrs {
 	u32			headerpadsz;
 	u32			max_rqst_sz;
@@ -23,18 +18,25 @@ struct nfs4_channel_attrs {
 };
 
 struct nfs4_slot {
-	u32		 	slot_nr;
 	u32		 	seq_nr;
-	unsigned long		flags;
 };
 
 struct nfs4_slot_table {
 	struct nfs4_slot 	*slots;
+	unsigned long		*used_slots;
+	unsigned long		_used_slots;	/* used when max_slots fits */
 	spinlock_t		slot_tbl_lock;
 	struct rpc_wait_queue	slot_tbl_waitq;
 	int			max_slots;
+	int			lowest_free_slotid;	/* lower bound hint */
+	int			highest_used_slotid;
 };
 
+static inline int slot_idx(struct nfs4_slot_table *tbl, struct nfs4_slot *sp)
+{
+	return sp - tbl->slots;
+}
+
 struct nfs4_channel {
 	struct nfs4_channel_attrs 	chan_attrs;
 	struct rpc_clnt 		*rpc_client;
-- 
1.5.3.3



More information about the pNFS mailing list