summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ipa/gsi.h9
-rw-r--r--drivers/net/ipa/gsi_trans.c89
2 files changed, 84 insertions, 14 deletions
diff --git a/drivers/net/ipa/gsi.h b/drivers/net/ipa/gsi.h
index 23de5f67374c..13468704c400 100644
--- a/drivers/net/ipa/gsi.h
+++ b/drivers/net/ipa/gsi.h
@@ -82,7 +82,14 @@ struct gsi_trans_pool {
struct gsi_trans_info {
atomic_t tre_avail; /* TREs available for allocation */
- struct gsi_trans_pool pool; /* transaction pool */
+
+ u16 free_id; /* first free trans in array */
+ u16 allocated_id; /* first allocated transaction */
+ u16 committed_id; /* first committed transaction */
+ u16 pending_id; /* first pending transaction */
+ u16 completed_id; /* first completed transaction */
+ u16 polled_id; /* first polled transaction */
+ struct gsi_trans *trans; /* transaction array */
struct gsi_trans **map; /* TRE -> transaction map */
struct gsi_trans_pool sg_pool; /* scatterlist pool */
diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c
index 18e7e8c405be..4eef1480c200 100644
--- a/drivers/net/ipa/gsi_trans.c
+++ b/drivers/net/ipa/gsi_trans.c
@@ -252,6 +252,9 @@ static void gsi_trans_move_committed(struct gsi_trans *trans)
list_move_tail(&trans->links, &trans_info->committed);
spin_unlock_bh(&trans_info->spinlock);
+
+ /* This allocated transaction is now committed */
+ trans_info->allocated_id++;
}
/* Move transactions from the committed list to the pending list */
@@ -259,7 +262,9 @@ static void gsi_trans_move_pending(struct gsi_trans *trans)
{
struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id];
struct gsi_trans_info *trans_info = &channel->trans_info;
+ u16 trans_index = trans - &trans_info->trans[0];
struct list_head list;
+ u16 delta;
spin_lock_bh(&trans_info->spinlock);
@@ -268,6 +273,10 @@ static void gsi_trans_move_pending(struct gsi_trans *trans)
list_splice_tail(&list, &trans_info->pending);
spin_unlock_bh(&trans_info->spinlock);
+
+ /* These committed transactions are now pending */
+ delta = trans_index - trans_info->committed_id + 1;
+ trans_info->committed_id += delta % channel->tre_count;
}
/* Move a transaction and all of its predecessors from the pending list
@@ -277,7 +286,9 @@ void gsi_trans_move_complete(struct gsi_trans *trans)
{
struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id];
struct gsi_trans_info *trans_info = &channel->trans_info;
+ u16 trans_index = trans - trans_info->trans;
struct list_head list;
+ u16 delta;
spin_lock_bh(&trans_info->spinlock);
@@ -286,6 +297,11 @@ void gsi_trans_move_complete(struct gsi_trans *trans)
list_splice_tail(&list, &trans_info->complete);
spin_unlock_bh(&trans_info->spinlock);
+
+ /* These pending transactions are now completed */
+ delta = trans_index - trans_info->pending_id + 1;
+ delta %= channel->tre_count;
+ trans_info->pending_id += delta;
}
/* Move a transaction from the completed list to the polled list */
@@ -293,12 +309,23 @@ void gsi_trans_move_polled(struct gsi_trans *trans)
{
struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id];
struct gsi_trans_info *trans_info = &channel->trans_info;
+ u16 trans_index;
spin_lock_bh(&trans_info->spinlock);
list_move_tail(&trans->links, &trans_info->polled);
+ trans = list_first_entry(&trans_info->polled,
+ struct gsi_trans, links);
+
spin_unlock_bh(&trans_info->spinlock);
+
+ /* This completed transaction is now polled */
+ trans_info->completed_id++;
+
+ WARN_ON(trans_info->polled_id == trans_info->completed_id);
+ trans_index = trans_info->polled_id % channel->tre_count;
+ WARN_ON(trans != &trans_info->trans[trans_index]);
}
/* Reserve some number of TREs on a channel. Returns true if successful */
@@ -343,20 +370,22 @@ struct gsi_trans *gsi_channel_trans_alloc(struct gsi *gsi, u32 channel_id,
struct gsi_channel *channel = &gsi->channel[channel_id];
struct gsi_trans_info *trans_info;
struct gsi_trans *trans;
+ u16 trans_index;
if (WARN_ON(tre_count > channel->trans_tre_max))
return NULL;
trans_info = &channel->trans_info;
- /* We reserve the TREs now, but consume them at commit time.
- * If there aren't enough available, we're done.
- */
+ /* If we can't reserve the TREs for the transaction, we're done */
if (!gsi_trans_tre_reserve(trans_info, tre_count))
return NULL;
- /* Allocate and initialize non-zero fields in the transaction */
- trans = gsi_trans_pool_alloc(&trans_info->pool, 1);
+ trans_index = trans_info->free_id % channel->tre_count;
+ trans = &trans_info->trans[trans_index];
+ memset(trans, 0, sizeof(*trans));
+
+ /* Initialize non-zero fields in the transaction */
trans->gsi = gsi;
trans->channel_id = channel_id;
trans->rsvd_count = tre_count;
@@ -367,6 +396,10 @@ struct gsi_trans *gsi_channel_trans_alloc(struct gsi *gsi, u32 channel_id,
sg_init_marker(trans->sgl, tre_count);
trans->direction = direction;
+ refcount_set(&trans->refcount, 1);
+
+ /* This free transaction will now be allocated */
+ trans_info->free_id++;
spin_lock_bh(&trans_info->spinlock);
@@ -374,16 +407,17 @@ struct gsi_trans *gsi_channel_trans_alloc(struct gsi *gsi, u32 channel_id,
spin_unlock_bh(&trans_info->spinlock);
- refcount_set(&trans->refcount, 1);
-
return trans;
}
/* Free a previously-allocated transaction */
void gsi_trans_free(struct gsi_trans *trans)
{
+ struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id];
refcount_t *refcount = &trans->refcount;
struct gsi_trans_info *trans_info;
+ struct gsi_trans *polled;
+ u16 trans_index;
bool last;
/* We must hold the lock to release the last reference */
@@ -399,13 +433,36 @@ void gsi_trans_free(struct gsi_trans *trans)
if (last)
list_del(&trans->links);
+ polled = list_first_entry_or_null(&trans_info->polled,
+ struct gsi_trans, links);
+
spin_unlock_bh(&trans_info->spinlock);
if (!last)
return;
- if (trans->used_count)
+ /* Unused transactions are allocated but never committed, pending,
+ * completed, or polled.
+ */
+ if (!trans->used_count) {
+ trans_info->allocated_id++;
+ trans_info->committed_id++;
+ trans_info->pending_id++;
+ trans_info->completed_id++;
+ } else {
ipa_gsi_trans_release(trans);
+ }
+
+ /* This transaction is now free */
+ trans_info->polled_id++;
+
+ if (polled) {
+ trans_index = trans_info->polled_id % channel->tre_count;
+ WARN_ON(polled != &trans_info->trans[trans_index]);
+ } else {
+ WARN_ON(trans_info->polled_id !=
+ trans_info->completed_id);
+ }
/* Releasing the reserved TREs implicitly frees the sgl[] and
* (if present) info[] arrays, plus the transaction itself.
@@ -736,10 +793,16 @@ int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id)
* modulo that number to determine the next one that's free.
* Transactions are allocated one at a time.
*/
- ret = gsi_trans_pool_init(&trans_info->pool, sizeof(struct gsi_trans),
- tre_max, 1);
- if (ret)
+ trans_info->trans = kcalloc(tre_count, sizeof(*trans_info->trans),
+ GFP_KERNEL);
+ if (!trans_info->trans)
return -ENOMEM;
+ trans_info->free_id = 0; /* all modulo channel->tre_count */
+ trans_info->allocated_id = 0;
+ trans_info->committed_id = 0;
+ trans_info->pending_id = 0;
+ trans_info->completed_id = 0;
+ trans_info->polled_id = 0;
/* A completion event contains a pointer to the TRE that caused
* the event (which will be the last one used by the transaction).
@@ -777,7 +840,7 @@ int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id)
err_map_free:
kfree(trans_info->map);
err_trans_free:
- gsi_trans_pool_exit(&trans_info->pool);
+ kfree(trans_info->trans);
dev_err(gsi->dev, "error %d initializing channel %u transactions\n",
ret, channel_id);
@@ -791,6 +854,6 @@ void gsi_channel_trans_exit(struct gsi_channel *channel)
struct gsi_trans_info *trans_info = &channel->trans_info;
gsi_trans_pool_exit(&trans_info->sg_pool);
- gsi_trans_pool_exit(&trans_info->pool);
+ kfree(trans_info->trans);
kfree(trans_info->map);
}