Commit 4ab6e359 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-ipa-small-transaction-updates'

Alex Elder says:

====================
net: ipa: small transaction updates

Version 2 of this series corrects a misspelling of "outstanding"
pointed out by the netdev test bots.  (For some reason I don't see
that when I run "checkpatch".)  I found and fixed a second instance
of that word being misspelled as well.

This series includes three changes to the transaction code.  The
first adds a new transaction list that represents a distinct state
that has not been maintained.  The second moves a field in the
transaction information structure, and reorders its initialization
a bit.  The third skips a function call when it is known not to be
necessary.

The last two are very small "leftover" patches.
====================

Link: https://lore.kernel.org/r/20220719181020.372697-1-elder@linaro.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents d79e4164 616c4a83
...@@ -720,6 +720,9 @@ static struct gsi_trans *gsi_channel_trans_last(struct gsi_channel *channel) ...@@ -720,6 +720,9 @@ static struct gsi_trans *gsi_channel_trans_last(struct gsi_channel *channel)
*/ */
if (channel->toward_ipa) { if (channel->toward_ipa) {
list = &trans_info->alloc; list = &trans_info->alloc;
if (!list_empty(list))
goto done;
list = &trans_info->committed;
if (!list_empty(list)) if (!list_empty(list))
goto done; goto done;
list = &trans_info->pending; list = &trans_info->pending;
...@@ -1364,7 +1367,7 @@ gsi_event_trans(struct gsi *gsi, struct gsi_event *event) ...@@ -1364,7 +1367,7 @@ gsi_event_trans(struct gsi *gsi, struct gsi_event *event)
* first *unfilled* event in the ring (following the last filled one). * first *unfilled* event in the ring (following the last filled one).
* *
* Events are sequential within the event ring, and transactions are * Events are sequential within the event ring, and transactions are
* sequential within the transaction pool. * sequential within the transaction array.
* *
* Note that @index always refers to an element *within* the event ring. * Note that @index always refers to an element *within* the event ring.
*/ */
......
...@@ -83,13 +83,15 @@ struct gsi_trans_pool { ...@@ -83,13 +83,15 @@ struct gsi_trans_pool {
struct gsi_trans_info { struct gsi_trans_info {
atomic_t tre_avail; /* TREs available for allocation */ atomic_t tre_avail; /* TREs available for allocation */
struct gsi_trans_pool pool; /* transaction pool */ struct gsi_trans_pool pool; /* transaction pool */
struct gsi_trans **map; /* TRE -> transaction map */
struct gsi_trans_pool sg_pool; /* scatterlist pool */ struct gsi_trans_pool sg_pool; /* scatterlist pool */
struct gsi_trans_pool cmd_pool; /* command payload DMA pool */ struct gsi_trans_pool cmd_pool; /* command payload DMA pool */
struct gsi_trans **map; /* TRE -> transaction map */
spinlock_t spinlock; /* protects updates to the lists */ spinlock_t spinlock; /* protects updates to the lists */
struct list_head alloc; /* allocated, not committed */ struct list_head alloc; /* allocated, not committed */
struct list_head pending; /* committed, awaiting completion */ struct list_head committed; /* committed, awaiting doorbell */
struct list_head pending; /* pending, awaiting completion */
struct list_head complete; /* completed, awaiting poll */ struct list_head complete; /* completed, awaiting poll */
struct list_head polled; /* returned by gsi_channel_poll_one() */ struct list_head polled; /* returned by gsi_channel_poll_one() */
}; };
...@@ -185,7 +187,7 @@ void gsi_teardown(struct gsi *gsi); ...@@ -185,7 +187,7 @@ void gsi_teardown(struct gsi *gsi);
* @gsi: GSI pointer * @gsi: GSI pointer
* @channel_id: Channel whose limit is to be returned * @channel_id: Channel whose limit is to be returned
* *
* Return: The maximum number of TREs oustanding on the channel * Return: The maximum number of TREs outstanding on the channel
*/ */
u32 gsi_channel_tre_max(struct gsi *gsi, u32 channel_id); u32 gsi_channel_tre_max(struct gsi *gsi, u32 channel_id);
......
...@@ -241,15 +241,31 @@ struct gsi_trans *gsi_channel_trans_complete(struct gsi_channel *channel) ...@@ -241,15 +241,31 @@ struct gsi_trans *gsi_channel_trans_complete(struct gsi_channel *channel)
struct gsi_trans, links); struct gsi_trans, links);
} }
/* Move a transaction from the allocated list to the pending list */ /* Move a transaction from the allocated list to the committed list */
static void gsi_trans_move_committed(struct gsi_trans *trans)
{
struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id];
struct gsi_trans_info *trans_info = &channel->trans_info;
spin_lock_bh(&trans_info->spinlock);
list_move_tail(&trans->links, &trans_info->committed);
spin_unlock_bh(&trans_info->spinlock);
}
/* Move transactions from the committed list to the pending list */
static void gsi_trans_move_pending(struct gsi_trans *trans) static void gsi_trans_move_pending(struct gsi_trans *trans)
{ {
struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id]; struct gsi_channel *channel = &trans->gsi->channel[trans->channel_id];
struct gsi_trans_info *trans_info = &channel->trans_info; struct gsi_trans_info *trans_info = &channel->trans_info;
struct list_head list;
spin_lock_bh(&trans_info->spinlock); spin_lock_bh(&trans_info->spinlock);
list_move_tail(&trans->links, &trans_info->pending); /* Move this transaction and all predecessors to the pending list */
list_cut_position(&list, &trans_info->committed, &trans->links);
list_splice_tail(&list, &trans_info->pending);
spin_unlock_bh(&trans_info->spinlock); spin_unlock_bh(&trans_info->spinlock);
} }
...@@ -346,7 +362,7 @@ struct gsi_trans *gsi_channel_trans_alloc(struct gsi *gsi, u32 channel_id, ...@@ -346,7 +362,7 @@ struct gsi_trans *gsi_channel_trans_alloc(struct gsi *gsi, u32 channel_id,
trans->rsvd_count = tre_count; trans->rsvd_count = tre_count;
init_completion(&trans->completion); init_completion(&trans->completion);
/* Allocate the scatterlist and (if requested) info entries. */ /* Allocate the scatterlist */
trans->sgl = gsi_trans_pool_alloc(&trans_info->sg_pool, tre_count); trans->sgl = gsi_trans_pool_alloc(&trans_info->sg_pool, tre_count);
sg_init_marker(trans->sgl, tre_count); sg_init_marker(trans->sgl, tre_count);
...@@ -388,6 +404,7 @@ void gsi_trans_free(struct gsi_trans *trans) ...@@ -388,6 +404,7 @@ void gsi_trans_free(struct gsi_trans *trans)
if (!last) if (!last)
return; return;
if (trans->used_count)
ipa_gsi_trans_release(trans); ipa_gsi_trans_release(trans);
/* Releasing the reserved TREs implicitly frees the sgl[] and /* Releasing the reserved TREs implicitly frees the sgl[] and
...@@ -581,13 +598,14 @@ static void __gsi_trans_commit(struct gsi_trans *trans, bool ring_db) ...@@ -581,13 +598,14 @@ static void __gsi_trans_commit(struct gsi_trans *trans, bool ring_db)
if (channel->toward_ipa) if (channel->toward_ipa)
gsi_trans_tx_committed(trans); gsi_trans_tx_committed(trans);
gsi_trans_move_pending(trans); gsi_trans_move_committed(trans);
/* Ring doorbell if requested, or if all TREs are allocated */ /* Ring doorbell if requested, or if all TREs are allocated */
if (ring_db || !atomic_read(&channel->trans_info.tre_avail)) { if (ring_db || !atomic_read(&channel->trans_info.tre_avail)) {
/* Report what we're handing off to hardware for TX channels */ /* Report what we're handing off to hardware for TX channels */
if (channel->toward_ipa) if (channel->toward_ipa)
gsi_trans_tx_queued(trans); gsi_trans_tx_queued(trans);
gsi_trans_move_pending(trans);
gsi_channel_doorbell(channel); gsi_channel_doorbell(channel);
} }
} }
...@@ -692,6 +710,7 @@ void gsi_trans_read_byte_done(struct gsi *gsi, u32 channel_id) ...@@ -692,6 +710,7 @@ void gsi_trans_read_byte_done(struct gsi *gsi, u32 channel_id)
int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id) int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id)
{ {
struct gsi_channel *channel = &gsi->channel[channel_id]; struct gsi_channel *channel = &gsi->channel[channel_id];
u32 tre_count = channel->tre_count;
struct gsi_trans_info *trans_info; struct gsi_trans_info *trans_info;
u32 tre_max; u32 tre_max;
int ret; int ret;
...@@ -699,30 +718,40 @@ int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id) ...@@ -699,30 +718,40 @@ int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id)
/* Ensure the size of a channel element is what's expected */ /* Ensure the size of a channel element is what's expected */
BUILD_BUG_ON(sizeof(struct gsi_tre) != GSI_RING_ELEMENT_SIZE); BUILD_BUG_ON(sizeof(struct gsi_tre) != GSI_RING_ELEMENT_SIZE);
/* The map array is used to determine what transaction is associated
* with a TRE that the hardware reports has completed. We need one
* map entry per TRE.
*/
trans_info = &channel->trans_info; trans_info = &channel->trans_info;
trans_info->map = kcalloc(channel->tre_count, sizeof(*trans_info->map),
GFP_KERNEL);
if (!trans_info->map)
return -ENOMEM;
/* We can't use more TREs than there are available in the ring. /* The tre_avail field is what ultimately limits the number of
* This limits the number of transactions that can be oustanding. * outstanding transactions and their resources. A transaction
* Worst case is one TRE per transaction (but we actually limit * allocation succeeds only if the TREs available are sufficient
* it to something a little less than that). We allocate resources * for what the transaction might need.
* for transactions (including transaction structures) based on
* this maximum number.
*/ */
tre_max = gsi_channel_tre_max(channel->gsi, channel_id); tre_max = gsi_channel_tre_max(channel->gsi, channel_id);
atomic_set(&trans_info->tre_avail, tre_max);
/* Transactions are allocated one at a time. */ /* We can't use more TREs than the number available in the ring.
* This limits the number of transactions that can be outstanding.
* Worst case is one TRE per transaction (but we actually limit
* it to something a little less than that). By allocating a
* power-of-two number of transactions we can use an index
* 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), ret = gsi_trans_pool_init(&trans_info->pool, sizeof(struct gsi_trans),
tre_max, 1); tre_max, 1);
if (ret) if (ret)
goto err_kfree; return -ENOMEM;
/* A completion event contains a pointer to the TRE that caused
* the event (which will be the last one used by the transaction).
* Each entry in this map records the transaction associated
* with a corresponding completed TRE.
*/
trans_info->map = kcalloc(tre_count, sizeof(*trans_info->map),
GFP_KERNEL);
if (!trans_info->map) {
ret = -ENOMEM;
goto err_trans_free;
}
/* A transaction uses a scatterlist array to represent the data /* A transaction uses a scatterlist array to represent the data
* transfers implemented by the transaction. Each scatterlist * transfers implemented by the transaction. Each scatterlist
...@@ -734,29 +763,21 @@ int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id) ...@@ -734,29 +763,21 @@ int gsi_channel_trans_init(struct gsi *gsi, u32 channel_id)
sizeof(struct scatterlist), sizeof(struct scatterlist),
tre_max, channel->trans_tre_max); tre_max, channel->trans_tre_max);
if (ret) if (ret)
goto err_trans_pool_exit; goto err_map_free;
/* Finally, the tre_avail field is what ultimately limits the number
* of outstanding transactions and their resources. A transaction
* allocation succeeds only if the TREs available are sufficient for
* what the transaction might need. Transaction resource pools are
* sized based on the maximum number of outstanding TREs, so there
* will always be resources available if there are TREs available.
*/
atomic_set(&trans_info->tre_avail, tre_max);
spin_lock_init(&trans_info->spinlock); spin_lock_init(&trans_info->spinlock);
INIT_LIST_HEAD(&trans_info->alloc); INIT_LIST_HEAD(&trans_info->alloc);
INIT_LIST_HEAD(&trans_info->committed);
INIT_LIST_HEAD(&trans_info->pending); INIT_LIST_HEAD(&trans_info->pending);
INIT_LIST_HEAD(&trans_info->complete); INIT_LIST_HEAD(&trans_info->complete);
INIT_LIST_HEAD(&trans_info->polled); INIT_LIST_HEAD(&trans_info->polled);
return 0; return 0;
err_trans_pool_exit: err_map_free:
gsi_trans_pool_exit(&trans_info->pool);
err_kfree:
kfree(trans_info->map); kfree(trans_info->map);
err_trans_free:
gsi_trans_pool_exit(&trans_info->pool);
dev_err(gsi->dev, "error %d initializing channel %u transactions\n", dev_err(gsi->dev, "error %d initializing channel %u transactions\n",
ret, channel_id); ret, channel_id);
......
...@@ -836,6 +836,8 @@ static int ipa_remove(struct platform_device *pdev) ...@@ -836,6 +836,8 @@ static int ipa_remove(struct platform_device *pdev)
kfree(ipa); kfree(ipa);
ipa_power_exit(power); ipa_power_exit(power);
dev_info(dev, "IPA driver removed");
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment