aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ipa/gsi_trans.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ipa/gsi_trans.c')
-rw-r--r--drivers/net/ipa/gsi_trans.c113
1 files changed, 97 insertions, 16 deletions
diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c
index 18e7e8c405be..05ab4d052c68 100644
--- a/drivers/net/ipa/gsi_trans.c
+++ b/drivers/net/ipa/gsi_trans.c
@@ -237,8 +237,24 @@ gsi_channel_trans_mapped(struct gsi_channel *channel, u32 index)
/* Return the oldest completed transaction for a channel (or null) */
struct gsi_trans *gsi_channel_trans_complete(struct gsi_channel *channel)
{
- return list_first_entry_or_null(&channel->trans_info.complete,
- struct gsi_trans, links);
+ struct gsi_trans_info *trans_info = &channel->trans_info;
+ u16 trans_id = trans_info->completed_id;
+ struct gsi_trans *trans;
+
+ trans = list_first_entry_or_null(&trans_info->complete,
+ struct gsi_trans, links);
+
+ if (!trans) {
+ WARN_ON(trans_id != trans_info->pending_id);
+ return NULL;
+ }
+
+ if (!WARN_ON(trans_id == trans_info->pending_id)) {
+ trans_id %= channel->tre_count;
+ WARN_ON(trans != &trans_info->trans[trans_id]);
+ }
+
+ return trans;
}
/* Move a transaction from the allocated list to the committed list */
@@ -252,6 +268,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 +278,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 +289,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 +302,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 +313,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 */
@@ -299,6 +331,9 @@ void gsi_trans_move_polled(struct gsi_trans *trans)
list_move_tail(&trans->links, &trans_info->polled);
spin_unlock_bh(&trans_info->spinlock);
+
+ /* This completed transaction is now polled */
+ trans_info->completed_id++;
}
/* Reserve some number of TREs on a channel. Returns true if successful */
@@ -343,20 +378,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 +404,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,8 +415,6 @@ 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;
}
@@ -404,8 +443,20 @@ void gsi_trans_free(struct gsi_trans *trans)
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++;
/* Releasing the reserved TREs implicitly frees the sgl[] and
* (if present) info[] arrays, plus the transaction itself.
@@ -655,6 +706,8 @@ void gsi_channel_trans_cancel_pending(struct gsi_channel *channel)
{
struct gsi_trans_info *trans_info = &channel->trans_info;
struct gsi_trans *trans;
+ struct gsi_trans *first;
+ struct gsi_trans *last;
bool cancelled;
/* channel->gsi->mutex is held by caller */
@@ -666,11 +719,33 @@ void gsi_channel_trans_cancel_pending(struct gsi_channel *channel)
list_splice_tail_init(&trans_info->pending, &trans_info->complete);
+ first = list_first_entry_or_null(&trans_info->complete,
+ struct gsi_trans, links);
+ last = list_last_entry_or_null(&trans_info->complete,
+ struct gsi_trans, links);
+
spin_unlock_bh(&trans_info->spinlock);
+ /* All pending transactions are now completed */
+ WARN_ON(cancelled != (trans_info->pending_id !=
+ trans_info->committed_id));
+
+ trans_info->pending_id = trans_info->committed_id;
+
/* Schedule NAPI polling to complete the cancelled transactions */
- if (cancelled)
+ if (cancelled) {
+ u16 trans_id;
+
napi_schedule(&channel->napi);
+
+ trans_id = trans_info->completed_id;
+ trans = &trans_info->trans[trans_id % channel->tre_count];
+ WARN_ON(trans != first);
+
+ trans_id = trans_info->pending_id - 1;
+ trans = &trans_info->trans[trans_id % channel->tre_count];
+ WARN_ON(trans != last);
+ }
}
/* Issue a command to read a single byte from a channel */
@@ -736,10 +811,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 +858,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 +872,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);
}